1f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa/* 2f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Copyright (C) 2009 The Android Open Source Project 3f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * 4f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License"); 5f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * you may not use this file except in compliance with the License. 6f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * You may obtain a copy of the License at 7f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * 8f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * http://www.apache.org/licenses/LICENSE-2.0 9f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * 10f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Unless required by applicable law or agreed to in writing, software 11f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS, 12f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * See the License for the specific language governing permissions and 14f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * limitations under the License. 15f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa */ 16f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawapackage android.pim.vcard; 17f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 18f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport android.content.ContentProviderOperation; 1969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawaimport android.pim.vcard.exception.VCardException; 2099a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Im; 21f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Phone; 22f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 23b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawaimport android.provider.ContactsContract.Data; 24f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawaimport android.telephony.PhoneNumberUtils; 25f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport android.text.TextUtils; 2669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawaimport android.util.Log; 27f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 2869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawaimport org.apache.commons.codec.DecoderException; 2969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawaimport org.apache.commons.codec.net.QuotedPrintableCodec; 3069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 3169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawaimport java.io.UnsupportedEncodingException; 3269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawaimport java.nio.ByteBuffer; 3369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawaimport java.nio.charset.Charset; 345c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawaimport java.util.ArrayList; 355a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawaimport java.util.Arrays; 36f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport java.util.Collection; 37f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport java.util.HashMap; 38f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport java.util.HashSet; 395c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawaimport java.util.List; 40f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport java.util.Map; 41f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawaimport java.util.Set; 42f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 43f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa/** 44f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Utilities for VCard handling codes. 45f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa */ 46f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawapublic class VCardUtils { 4769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa private static final String LOG_TAG = "VCardUtils"; 4869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 49f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is 506cb76ac37f1b4a36d81db7d7e8652d82b6f1b8b5Daisuke Miyakawa // converted to two parameter Strings. These only contain some minor fields valid in both 51f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa // vCard and current (as of 2009-08-07) Contacts structure. 52f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS; 535a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa private static final Set<String> sPhoneTypesUnknownToContactsSet; 5499a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa private static final Map<String, Integer> sKnownPhoneTypeMap_StoI; 5599a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa private static final Map<Integer, String> sKnownImPropNameMap_ItoS; 565a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa private static final Set<String> sMobilePhoneLabelSet; 57021de736237f6d408d61c1c4f884f98125cc081aDaisuke Miyakawa 58f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa static { 59f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>(); 6099a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>(); 61f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 621b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR); 631b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR); 641b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER); 651b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER); 661b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN); 671b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN); 68f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 691b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME); 701b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK); 711b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE); 72f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 731b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER); 741b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK, 751b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa Phone.TYPE_CALLBACK); 7699a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put( 771b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN); 781b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO); 791b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD, 801b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa Phone.TYPE_TTY_TDD); 811b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT, 82021de736237f6d408d61c1c4f884f98125cc081aDaisuke Miyakawa Phone.TYPE_ASSISTANT); 83f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 845a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa sPhoneTypesUnknownToContactsSet = new HashSet<String>(); 851b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM); 86839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG); 871b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS); 881b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO); 8999a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa 9099a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa sKnownImPropNameMap_ItoS = new HashMap<Integer, String>(); 911b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM); 921b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN); 931b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO); 941b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME); 951b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK, 961b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa VCardConstants.PROPERTY_X_GOOGLE_TALK); 971b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ); 981b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER); 991b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ); 1001b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING); 1015a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa 1025a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa // \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone) 1035a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa // \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone) 1045a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa // \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone) 1055a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa // \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone) 1065a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa sMobilePhoneLabelSet = new HashSet<String>(Arrays.asList( 1075a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa "MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4", 1085a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa "\uFF79\uFF72\uFF80\uFF72")); 109f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 110021de736237f6d408d61c1c4f884f98125cc081aDaisuke Miyakawa 1116cb76ac37f1b4a36d81db7d7e8652d82b6f1b8b5Daisuke Miyakawa public static String getPhoneTypeString(Integer type) { 112f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return sKnownPhoneTypesMap_ItoS.get(type); 113f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 114021de736237f6d408d61c1c4f884f98125cc081aDaisuke Miyakawa 115f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa /** 116f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Returns Interger when the given types can be parsed as known type. Returns String object 117f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * when not, which should be set to label. 118f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa */ 11926ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa public static Object getPhoneTypeFromStrings(Collection<String> types, 12026ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa String number) { 12126ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa if (number == null) { 12226ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa number = ""; 12326ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa } 124f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa int type = -1; 125f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa String label = null; 126f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa boolean isFax = false; 127f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa boolean hasPref = false; 128f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 129f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (types != null) { 130f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa for (String typeString : types) { 1315a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa if (typeString == null) { 1325a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa continue; 1335a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 1345a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa typeString = typeString.toUpperCase(); 1351b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { 136f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa hasPref = true; 1371b9e2bec6381ac7e7d0cfe0db549c5396d2ba7ceDaisuke Miyakawa } else if (typeString.equals(VCardConstants.PARAM_TYPE_FAX)) { 138f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa isFax = true; 139f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else { 140f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (typeString.startsWith("X-") && type < 0) { 141f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa typeString = typeString.substring(2); 142f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 1435a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa if (typeString.length() == 0) { 1445a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa continue; 1455a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 1465a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa final Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString); 147f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (tmp != null) { 14826ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa final int typeCandidate = tmp; 14926ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa // TYPE_PAGER is prefered when the number contains @ surronded by 15026ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa // a pager number and a domain name. 15126ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa // e.g. 15226ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa // o 1111@domain.com 15326ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa // x @domain.com 15426ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa // x 1111@ 15526ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa final int indexOfAt = number.indexOf("@"); 15626ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa if ((typeCandidate == Phone.TYPE_PAGER 15726ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa && 0 < indexOfAt && indexOfAt < number.length() - 1) 15826ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa || type < 0 15926ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa || type == Phone.TYPE_CUSTOM) { 16026ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa type = tmp; 16126ee0ae9de37e50b790a0a7b7bddb1d27d5b1214Daisuke Miyakawa } 162f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else if (type < 0) { 163f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa type = Phone.TYPE_CUSTOM; 164f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa label = typeString; 165f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 166f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 167f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 168f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 169f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (type < 0) { 170f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (hasPref) { 171f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa type = Phone.TYPE_MAIN; 172f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else { 173f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa // default to TYPE_HOME 174f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa type = Phone.TYPE_HOME; 175f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 176f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 177f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (isFax) { 178f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (type == Phone.TYPE_HOME) { 179f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa type = Phone.TYPE_FAX_HOME; 180f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else if (type == Phone.TYPE_WORK) { 181f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa type = Phone.TYPE_FAX_WORK; 182f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else if (type == Phone.TYPE_OTHER) { 183f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa type = Phone.TYPE_OTHER_FAX; 184f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 185f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 186f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (type == Phone.TYPE_CUSTOM) { 187f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return label; 188f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else { 189f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return type; 190f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 191f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 19299a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa 1935a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa @SuppressWarnings("deprecation") 1945a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa public static boolean isMobilePhoneLabel(final String label) { 1955a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa // For backward compatibility. 1965a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa // Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now. 1975a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa // To support mobile type at that time, this custom label had been used. 19869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label)); 19999a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa } 20099a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa 2015a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) { 2025a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa return sPhoneTypesUnknownToContactsSet.contains(label); 203f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 2045a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa 2055a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa public static String getPropertyNameForIm(final int protocol) { 2065a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa return sKnownImPropNameMap_ItoS.get(protocol); 2075a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 2085a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa 209592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa public static String[] sortNameElements(final int vcardType, 210592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final String familyName, final String middleName, final String givenName) { 211592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final String[] list = new String[3]; 212592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final int nameOrderType = VCardConfig.getNameOrderType(vcardType); 213592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa switch (nameOrderType) { 214592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa case VCardConfig.NAME_ORDER_JAPANESE: { 215592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa if (containsOnlyPrintableAscii(familyName) && 216592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa containsOnlyPrintableAscii(givenName)) { 217592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[0] = givenName; 218592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[1] = middleName; 219592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[2] = familyName; 220592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa } else { 221592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[0] = familyName; 222592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[1] = middleName; 223592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[2] = givenName; 224592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa } 225592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa break; 226592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa } 227592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa case VCardConfig.NAME_ORDER_EUROPE: { 228592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[0] = middleName; 229592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[1] = givenName; 230592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[2] = familyName; 231592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa break; 232592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa } 233592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa default: { 234592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[0] = givenName; 235592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[1] = middleName; 236592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa list[2] = familyName; 237592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa break; 238592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa } 239f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 240f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return list; 241f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 242f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 243f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa public static int getPhoneNumberFormat(final int vcardType) { 244f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa if (VCardConfig.isJapaneseDevice(vcardType)) { 245f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa return PhoneNumberUtils.FORMAT_JAPAN; 246f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa } else { 247f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa return PhoneNumberUtils.FORMAT_NANP; 248f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa } 249f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa } 250f2ad61c83da509270b8c5e9583b503ced928315dDaisuke Miyakawa 251f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa /** 25269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 253f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Inserts postal data into the builder object. 25469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 25569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 256f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Note that the data structure of ContactsContract is different from that defined in vCard. 257c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa * So some conversion may be performed in this method. 25869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 259f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa */ 260f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa public static void insertStructuredPostalDataUsingContactsStruct(int vcardType, 261f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa final ContentProviderOperation.Builder builder, 2625a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa final VCardEntry.PostalData postalData) { 263f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0); 264f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 265f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 266f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.TYPE, postalData.type); 267f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (postalData.type == StructuredPostal.TYPE_CUSTOM) { 268f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.LABEL, postalData.label); 269f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 270f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 271a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa final String streetString; 272a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa if (TextUtils.isEmpty(postalData.street)) { 273a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa if (TextUtils.isEmpty(postalData.extendedAddress)) { 274a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa streetString = null; 275a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa } else { 276a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa streetString = postalData.extendedAddress; 277a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa } 278a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa } else { 279a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa if (TextUtils.isEmpty(postalData.extendedAddress)) { 280a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa streetString = postalData.street; 281a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa } else { 282a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa streetString = postalData.street + " " + postalData.extendedAddress; 283a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa } 284a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa } 285f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.POBOX, postalData.pobox); 286a6d81e3ee4cf12ade1ef6701b5f1fcdd390fc98aDaisuke Miyakawa builder.withValue(StructuredPostal.STREET, streetString); 287f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.CITY, postalData.localty); 288f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.REGION, postalData.region); 289f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode); 290f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.COUNTRY, postalData.country); 291f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 292f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(StructuredPostal.FORMATTED_ADDRESS, 293f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa postalData.getFormattedAddress(vcardType)); 294f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (postalData.isPrimary) { 295f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.withValue(Data.IS_PRIMARY, 1); 296f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 297f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 29899a0a2cd73503513891565a4aaf99e209bd262d2Daisuke Miyakawa 299592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa public static String constructNameFromElements(final int vcardType, 300592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final String familyName, final String middleName, final String givenName) { 301592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa return constructNameFromElements(vcardType, familyName, middleName, givenName, 302f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa null, null); 303f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 304f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa 305592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa public static String constructNameFromElements(final int vcardType, 306592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final String familyName, final String middleName, final String givenName, 307592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final String prefix, final String suffix) { 308592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final StringBuilder builder = new StringBuilder(); 309592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName); 310f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa boolean first = true; 311f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (!TextUtils.isEmpty(prefix)) { 312f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa first = false; 313f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.append(prefix); 314f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 315592988d307e8d305ca163c4e58da0fb350054194Daisuke Miyakawa for (final String namePart : nameList) { 316f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (!TextUtils.isEmpty(namePart)) { 317f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (first) { 318f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa first = false; 319f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else { 320f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.append(' '); 321f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 322f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.append(namePart); 323f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 324f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 325f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (!TextUtils.isEmpty(suffix)) { 326f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (!first) { 327f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.append(' '); 328f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 329f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.append(suffix); 330f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 331f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return builder.toString(); 332f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 3335c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa 33469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa /** 33569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * Splits the given value into pieces using the delimiter ';' inside it. 33669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * 33769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * Escaped characters in those values are automatically unescaped into original form. 33869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa */ 3395c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa public static List<String> constructListFromValue(final String value, 34069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final int vcardType) { 3415c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa final List<String> list = new ArrayList<String>(); 3425c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa StringBuilder builder = new StringBuilder(); 34369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final int length = value.length(); 3445c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa for (int i = 0; i < length; i++) { 3455c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa char ch = value.charAt(i); 3465c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa if (ch == '\\' && i < length - 1) { 3475c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa char nextCh = value.charAt(i + 1); 34869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final String unescapedString; 34969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (VCardConfig.isVersion40(vcardType)) { 35069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa unescapedString = VCardParserImpl_V40.unescapeCharacter(nextCh); 35169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else if (VCardConfig.isVersion30(vcardType)) { 35269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa unescapedString = VCardParserImpl_V30.unescapeCharacter(nextCh); 35369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else { 35469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (!VCardConfig.isVersion21(vcardType)) { 35569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa // Unknown vCard type 35669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa Log.w(LOG_TAG, "Unknown vCard type"); 35769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 35869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa unescapedString = VCardParserImpl_V21.unescapeCharacter(nextCh); 35969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 36069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 3615c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa if (unescapedString != null) { 3625c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa builder.append(unescapedString); 3635c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa i++; 3645c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa } else { 3655c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa builder.append(ch); 3665c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa } 3675c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa } else if (ch == ';') { 3685c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa list.add(builder.toString()); 3695c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa builder = new StringBuilder(); 3705c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa } else { 3715c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa builder.append(ch); 3725c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa } 3735c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa } 3745c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa list.add(builder.toString()); 3755c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa return list; 3765c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa } 3775c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa 378c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa public static boolean containsOnlyPrintableAscii(final String...values) { 379c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (values == null) { 380f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return true; 381f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 382a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return containsOnlyPrintableAscii(Arrays.asList(values)); 383a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 384a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa 385a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa public static boolean containsOnlyPrintableAscii(final Collection<String> values) { 386a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa if (values == null) { 387a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return true; 388a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 389c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa for (final String value : values) { 390c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (TextUtils.isEmpty(value)) { 391c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa continue; 392c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 393973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa if (!TextUtils.isPrintableAsciiOnly(value)) { 394973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa return false; 395f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 396f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 397f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return true; 398f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 399a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa 400a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa /** 40169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 402a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa * This is useful when checking the string should be encoded into quoted-printable 403a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa * or not, which is required by vCard 2.1. 40469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 40569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 406a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa * See the definition of "7bit" in vCard 2.1 spec for more information. 40769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 408a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa */ 409c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) { 410c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (values == null) { 411a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa return true; 412a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa } 413a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values)); 414a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 415a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa 416a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa public static boolean containsOnlyNonCrLfPrintableAscii(final Collection<String> values) { 417a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa if (values == null) { 418a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return true; 419a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 420c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa final int asciiFirst = 0x20; 421c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa final int asciiLast = 0x7E; // included 422c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa for (final String value : values) { 423c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (TextUtils.isEmpty(value)) { 424c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa continue; 425c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 426c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa final int length = value.length(); 427c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { 428c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa final int c = value.codePointAt(i); 429c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (!(asciiFirst <= c && c <= asciiLast)) { 430c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa return false; 431c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 432c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 433c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 434c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa return true; 435c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 436c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa 437c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa private static final Set<Character> sUnAcceptableAsciiInV21WordSet = 438c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' ')); 439a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa 440c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa /** 44169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 442f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * This is useful since vCard 3.0 often requires the ("X-") properties and groups 443f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * should contain only alphabets, digits, and hyphen. 44469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 44569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 446f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * Note: It is already known some devices (wrongly) outputs properties with characters 447a2ba9a199962eb1b14ac3e0aa0fbc4c557298c96Daisuke Miyakawa * which should not be in the field. One example is "X-GOOGLE TALK". We accept 448f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa * such kind of input but must never output it unless the target is very specific 44969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * to the device which is able to parse the malformed input. 45069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 451f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa */ 452c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa public static boolean containsOnlyAlphaDigitHyphen(final String...values) { 453c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (values == null) { 454f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return true; 455f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 456a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return containsOnlyAlphaDigitHyphen(Arrays.asList(values)); 457a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 458a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa 459a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa public static boolean containsOnlyAlphaDigitHyphen(final Collection<String> values) { 460a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa if (values == null) { 461a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return true; 462a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 463839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa final int upperAlphabetFirst = 0x41; // A 464839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa final int upperAlphabetAfterLast = 0x5b; // [ 465839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa final int lowerAlphabetFirst = 0x61; // a 466839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa final int lowerAlphabetAfterLast = 0x7b; // { 467839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa final int digitFirst = 0x30; // 0 468839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa final int digitAfterLast = 0x3A; // : 469f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa final int hyphen = '-'; 470c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa for (final String str : values) { 471c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (TextUtils.isEmpty(str)) { 472c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa continue; 473c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 474c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa final int length = str.length(); 475c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { 476c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa int codepoint = str.codePointAt(i); 477c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) || 478839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) || 479839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa (digitFirst <= codepoint && codepoint < digitAfterLast) || 480f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa (codepoint == hyphen))) { 481c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa return false; 482c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 483f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 484f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 485f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return true; 486f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 487a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa 48869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa public static boolean containsOnlyWhiteSpaces(final String...values) { 48969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (values == null) { 49069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return true; 49169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 49269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return containsOnlyWhiteSpaces(Arrays.asList(values)); 49369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 49469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 49569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa public static boolean containsOnlyWhiteSpaces(final Collection<String> values) { 49669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (values == null) { 49769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return true; 49869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 49969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa for (final String str : values) { 50069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (TextUtils.isEmpty(str)) { 50169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa continue; 50269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 50369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final int length = str.length(); 50469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { 50569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (!Character.isWhitespace(str.codePointAt(i))) { 50669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return false; 50769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 50869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 50969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 51069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return true; 51169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 51269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 513a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa /** 51469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 515a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa * Returns true when the given String is categorized as "word" specified in vCard spec 2.1. 51669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 51769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * <p> 51869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * vCard 2.1 specifies:<br /> 519a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa * word = <any printable 7bit us-ascii except []=:., > 52069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * </p> 521a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa */ 522a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa public static boolean isV21Word(final String value) { 523a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa if (TextUtils.isEmpty(value)) { 524a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return true; 525a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 526a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa final int asciiFirst = 0x20; 527a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa final int asciiLast = 0x7E; // included 528a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa final int length = value.length(); 529a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { 530a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa final int c = value.codePointAt(i); 531a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa if (!(asciiFirst <= c && c <= asciiLast) || 532a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa sUnAcceptableAsciiInV21WordSet.contains((char)c)) { 533a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return false; 534a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 535a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 536a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa return true; 537a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa } 538a750fdd765ec253ffa8bf3d4848d5c3a35e1979bDaisuke Miyakawa 53969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa private static final int[] sEscapeIndicatorsV30 = new int[]{ 54069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa ':', ';', ',', ' ' 54169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa }; 54269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 54369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa private static final int[] sEscapeIndicatorsV40 = new int[]{ 54469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa ';', ':' 54569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa }; 54669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 547b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa /** 548b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * <P> 549b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * Returns String available as parameter value in vCard 3.0. 550b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * </P> 551b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * <P> 552b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * RFC 2426 requires vCard composer to quote parameter values when it contains 553b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * semi-colon, for example (See RFC 2426 for more information). 554b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * This method checks whether the given String can be used without quotes. 555b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * </P> 556b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * <P> 55769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * Note: We remove DQUOTE inside the given value silently for now. 558b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa * </P> 559b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa */ 56069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa public static String toStringAsV30ParamValue(String value) { 56169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return toStringAsParamValue(value, sEscapeIndicatorsV30); 56269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 56369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 56469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa public static String toStringAsV40ParamValue(String value) { 56569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return toStringAsParamValue(value, sEscapeIndicatorsV40); 56669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 56769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 56869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa private static String toStringAsParamValue(String value, final int[] escapeIndicators) { 569b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa if (TextUtils.isEmpty(value)) { 570b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa value = ""; 571b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa } 572b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa final int asciiFirst = 0x20; 573b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa final int asciiLast = 0x7E; // included 574b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 575b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa final int length = value.length(); 576b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa boolean needQuote = false; 577b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { 578b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa final int codePoint = value.codePointAt(i); 579b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa if (codePoint < asciiFirst || codePoint == '"') { 580b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa // CTL characters and DQUOTE are never accepted. Remove them. 581b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa continue; 582b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa } 583b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa builder.appendCodePoint(codePoint); 58469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa for (int indicator : escapeIndicators) { 58569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (codePoint == indicator) { 58669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa needQuote = true; 58769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa break; 58869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 589b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa } 590b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa } 59169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 592b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa final String result = builder.toString(); 59369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return ((result.isEmpty() || VCardUtils.containsOnlyWhiteSpaces(result)) 59469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa ? "" 59569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa : (needQuote ? ('"' + result + '"') 59669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa : result)); 597b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa } 598b7688558cc30bceac8377640db68126e53dd545eDaisuke Miyakawa 599c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa public static String toHalfWidthString(final String orgString) { 600f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (TextUtils.isEmpty(orgString)) { 601f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return null; 602f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 6033d745c2b925ccc7c6591dbb3dfd21649782af991Daisuke Miyakawa final StringBuilder builder = new StringBuilder(); 6043d745c2b925ccc7c6591dbb3dfd21649782af991Daisuke Miyakawa final int length = orgString.length(); 605c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) { 606f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa // All Japanese character is able to be expressed by char. 607f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa // Do not need to use String#codepPointAt(). 6083d745c2b925ccc7c6591dbb3dfd21649782af991Daisuke Miyakawa final char ch = orgString.charAt(i); 609c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch); 610f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa if (halfWidthText != null) { 611f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.append(halfWidthText); 612f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } else { 613f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa builder.append(ch); 614f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 615f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 616f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa return builder.toString(); 617f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 61849c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa 61949c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa /** 62049c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa * Guesses the format of input image. Currently just the first few bytes are used. 62149c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa * The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when 62249c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa * the guess failed. 62349c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa * @param input Image as byte array. 62449c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa * @return The image type or null when the type cannot be determined. 62549c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa */ 62649c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa public static String guessImageType(final byte[] input) { 627c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (input == null) { 628c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa return null; 629c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 63049c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') { 63149c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa return "GIF"; 63249c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa } else if (input.length >= 4 && input[0] == (byte) 0x89 63349c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa && input[1] == 'P' && input[2] == 'N' && input[3] == 'G') { 63449c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa // Note: vCard 2.1 officially does not support PNG, but we may have it and 63549c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa // using X- word like "X-PNG" may not let importers know it is PNG. 63649c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa // So we use the String "PNG" as is... 63749c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa return "PNG"; 63849c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa } else if (input.length >= 2 && input[0] == (byte) 0xff 63949c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa && input[1] == (byte) 0xd8) { 64049c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa return "JPEG"; 64149c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa } else { 64249c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa return null; 64349c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa } 64449c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa } 64549c0decf46d4f7082a17e595fba2c501a8369452Daisuke Miyakawa 646c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa /** 647c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa * @return True when all the given values are null or empty Strings. 648c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa */ 649c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa public static boolean areAllEmpty(final String...values) { 650c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (values == null) { 651c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa return true; 652c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 653c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa 654c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa for (final String value : values) { 655c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa if (!TextUtils.isEmpty(value)) { 656c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa return false; 657c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 658c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 659c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa return true; 660c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa } 661c4b51712d89e9fb742b598911e611c80ad51de82Daisuke Miyakawa 66269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa //// The methods bellow may be used by unit test. 66369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 66469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa /** 66569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa * Unquotes given Quoted-Printable value. value must not be null. 66669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa */ 66769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa public static String parseQuotedPrintable( 66869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final String value, boolean strictLineBreaking, 66969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa String sourceCharset, String targetCharset) { 67069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa // "= " -> " ", "=\t" -> "\t". 67169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa // Previous code had done this replacement. Keep on the safe side. 67269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final String quotedPrintable; 67369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa { 67469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 67569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final int length = value.length(); 67669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa for (int i = 0; i < length; i++) { 67769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa char ch = value.charAt(i); 67869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (ch == '=' && i < length - 1) { 67969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa char nextCh = value.charAt(i + 1); 68069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (nextCh == ' ' || nextCh == '\t') { 68169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa builder.append(nextCh); 68269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa i++; 68369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa continue; 68469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 68569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 68669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa builder.append(ch); 68769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 68869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa quotedPrintable = builder.toString(); 68969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 69069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 69169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa String[] lines; 69269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (strictLineBreaking) { 69369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa lines = quotedPrintable.split("\r\n"); 69469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else { 69569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa StringBuilder builder = new StringBuilder(); 69669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final int length = quotedPrintable.length(); 69769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa ArrayList<String> list = new ArrayList<String>(); 69869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa for (int i = 0; i < length; i++) { 69969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa char ch = quotedPrintable.charAt(i); 70069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (ch == '\n') { 70169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa list.add(builder.toString()); 70269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa builder = new StringBuilder(); 70369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else if (ch == '\r') { 70469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa list.add(builder.toString()); 70569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa builder = new StringBuilder(); 70669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (i < length - 1) { 70769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa char nextCh = quotedPrintable.charAt(i + 1); 70869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (nextCh == '\n') { 70969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa i++; 71069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 71169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 71269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else { 71369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa builder.append(ch); 71469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 71569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 71669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final String lastLine = builder.toString(); 71769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (lastLine.length() > 0) { 71869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa list.add(lastLine); 71969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 72069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa lines = list.toArray(new String[0]); 72169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 72269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 72369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 72469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa for (String line : lines) { 72569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (line.endsWith("=")) { 72669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa line = line.substring(0, line.length() - 1); 72769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 72869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa builder.append(line); 72969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 73069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 73169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final String rawString = builder.toString(); 73269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (TextUtils.isEmpty(rawString)) { 73369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa Log.w(LOG_TAG, "Given raw string is empty."); 73469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 73569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 73669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa byte[] rawBytes = null; 73769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa try { 73869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa rawBytes = rawString.getBytes(sourceCharset); 73969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } catch (UnsupportedEncodingException e) { 74069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa Log.w(LOG_TAG, "Failed to decode: " + sourceCharset); 74169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa rawBytes = rawString.getBytes(); 74269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 74369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 74469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa byte[] decodedBytes = null; 74569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa try { 74669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa decodedBytes = QuotedPrintableCodec.decodeQuotedPrintable(rawBytes); 74769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } catch (DecoderException e) { 74869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa Log.e(LOG_TAG, "DecoderException is thrown."); 74969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa decodedBytes = rawBytes; 75069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 75169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 75269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa try { 75369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return new String(decodedBytes, targetCharset); 75469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } catch (UnsupportedEncodingException e) { 75569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); 75669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return new String(decodedBytes); 75769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 75869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 75969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 76069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa public static final VCardParser getAppropriateParser(int vcardType) 76169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa throws VCardException { 76269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (VCardConfig.isVersion21(vcardType)) { 76369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return new VCardParser_V21(); 76469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else if (VCardConfig.isVersion30(vcardType)) { 76569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return new VCardParser_V30(); 76669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else if (VCardConfig.isVersion40(vcardType)) { 76769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return new VCardParser_V40(); 76869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } else { 76969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa throw new VCardException("Version is not specified"); 77069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 77169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 77269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 77369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa public static final String convertStringCharset( 77469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa String originalString, String sourceCharset, String targetCharset) { 77569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa if (sourceCharset.equalsIgnoreCase(targetCharset)) { 77669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return originalString; 77769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 77869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final Charset charset = Charset.forName(sourceCharset); 77969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final ByteBuffer byteBuffer = charset.encode(originalString); 78069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa // byteBuffer.array() "may" return byte array which is larger than 78169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa // byteBuffer.remaining(). Here, we keep on the safe side. 78269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa final byte[] bytes = new byte[byteBuffer.remaining()]; 78369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa byteBuffer.get(bytes); 78469831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa try { 78569831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return new String(bytes, targetCharset); 78669831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } catch (UnsupportedEncodingException e) { 78769831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); 78869831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa return null; 78969831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 79069831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa } 79169831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 79269831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa // TODO: utilities for vCard 4.0: datetime, timestamp, integer, float, and boolean 79369831d9dc16c1d36739328910e5d7c0fb7d327feDaisuke Miyakawa 794f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa private VCardUtils() { 795f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa } 796f4ddea769098e24a7316b9ee895d323005433c2cDaisuke Miyakawa} 797