14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* 24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2009 The Android Open Source Project 34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License"); 54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * you may not use this file except in compliance with the License. 64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * You may obtain a copy of the License at 74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * http://www.apache.org/licenses/LICENSE-2.0 94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software 114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS, 124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the License for the specific language governing permissions and 144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * limitations under the License. 154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.vcard; 174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Im; 194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Phone; 204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.telephony.PhoneNumberUtils; 219919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawaimport android.text.SpannableStringBuilder; 224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.text.TextUtils; 234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.util.Log; 244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 25d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Chengimport com.android.vcard.exception.VCardException; 26d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng 272c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawaimport java.io.ByteArrayOutputStream; 284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.UnsupportedEncodingException; 2958ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawaimport java.nio.ByteBuffer; 3058ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawaimport java.nio.charset.Charset; 314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList; 324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Arrays; 334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Collection; 344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashMap; 354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet; 364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.List; 374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Map; 384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set; 394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/** 414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Utilities for VCard handling codes. 424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapublic class VCardUtils { 4402117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa private static final String LOG_TAG = VCardConstants.LOG_TAG; 454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 462c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa /** 472c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa * See org.apache.commons.codec.DecoderException 482c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa */ 492c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa private static class DecoderException extends Exception { 502c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa public DecoderException(String pMessage) { 512c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa super(pMessage); 522c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 532c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 542c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa 552c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa /** 562c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa * See org.apache.commons.codec.net.QuotedPrintableCodec 572c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa */ 582c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa private static class QuotedPrintableCodecPort { 592c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa private static byte ESCAPE_CHAR = '='; 602c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa public static final byte[] decodeQuotedPrintable(byte[] bytes) 612c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa throws DecoderException { 622c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa if (bytes == null) { 632c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa return null; 642c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 652c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 662c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa for (int i = 0; i < bytes.length; i++) { 672c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa int b = bytes[i]; 682c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa if (b == ESCAPE_CHAR) { 692c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa try { 702c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa int u = Character.digit((char) bytes[++i], 16); 712c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa int l = Character.digit((char) bytes[++i], 16); 722c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa if (u == -1 || l == -1) { 732c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa throw new DecoderException("Invalid quoted-printable encoding"); 742c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 752c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa buffer.write((char) ((u << 4) + l)); 762c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } catch (ArrayIndexOutOfBoundsException e) { 772c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa throw new DecoderException("Invalid quoted-printable encoding"); 782c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 792c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } else { 802c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa buffer.write(b); 812c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 822c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 832c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa return buffer.toByteArray(); 842c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 852c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa } 862c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa 879919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa /** 889919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa * Ported methods which are hidden in {@link PhoneNumberUtils}. 899919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa */ 909919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa public static class PhoneNumberUtilsPort { 919919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa public static String formatNumber(String source, int defaultFormattingType) { 929919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa final SpannableStringBuilder text = new SpannableStringBuilder(source); 939919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa PhoneNumberUtils.formatNumber(text, defaultFormattingType); 949919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa return text.toString(); 959919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa } 969919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa } 979919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa 989919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa /** 999919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa * Ported methods which are hidden in {@link TextUtils}. 1009919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa */ 1019919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa public static class TextUtilsPort { 1029919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa public static boolean isPrintableAscii(final char c) { 1039919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa final int asciiFirst = 0x20; 1049919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa final int asciiLast = 0x7E; // included 1059919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa return (asciiFirst <= c && c <= asciiLast) || c == '\r' || c == '\n'; 1069919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa } 1079919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa 1089919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa public static boolean isPrintableAsciiOnly(final CharSequence str) { 1099919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa final int len = str.length(); 1109919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa for (int i = 0; i < len; i++) { 1119919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa if (!isPrintableAscii(str.charAt(i))) { 1129919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa return false; 1139919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa } 1149919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa } 1159919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa return true; 1169919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa } 1179919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa } 1189919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa 1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is 1204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // converted to two parameter Strings. These only contain some minor fields valid in both 12102117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa // vCard and current (as of 2009-08-07) Contacts structure. 1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS; 1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final Set<String> sPhoneTypesUnknownToContactsSet; 1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final Map<String, Integer> sKnownPhoneTypeMap_StoI; 1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final Map<Integer, String> sKnownImPropNameMap_ItoS; 1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final Set<String> sMobilePhoneLabelSet; 1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static { 1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>(); 1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>(); 1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR); 1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR); 1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER); 1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER); 1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN); 1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN); 13802117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa 1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME); 1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK); 1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE); 14202117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa 1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER); 1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK, 1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Phone.TYPE_CALLBACK); 1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put( 1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN); 1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO); 1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD, 1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Phone.TYPE_TTY_TDD); 1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT, 1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Phone.TYPE_ASSISTANT); 153449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // OTHER (default in Android) should correspond to VOICE (default in vCard). 154449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_VOICE, Phone.TYPE_OTHER); 1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sPhoneTypesUnknownToContactsSet = new HashSet<String>(); 1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM); 1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG); 1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS); 1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO); 1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS = new HashMap<Integer, String>(); 1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM); 1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN); 1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO); 1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME); 1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK, 1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa VCardConstants.PROPERTY_X_GOOGLE_TALK); 1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ); 1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER); 1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ); 1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING); 1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone) 1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone) 1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone) 1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone) 1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sMobilePhoneLabelSet = new HashSet<String>(Arrays.asList( 1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4", 1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "\uFF79\uFF72\uFF80\uFF72")); 1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static String getPhoneTypeString(Integer type) { 1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return sKnownPhoneTypesMap_ItoS.get(type); 1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Returns Interger when the given types can be parsed as known type. Returns String object 18902117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa * when not, which should be set to label. 1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static Object getPhoneTypeFromStrings(Collection<String> types, 1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String number) { 1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (number == null) { 1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa number = ""; 1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int type = -1; 1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String label = null; 1984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa boolean isFax = false; 1994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa boolean hasPref = false; 20002117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa 2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (types != null) { 20200b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa for (final String typeStringOrg : types) { 20300b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa if (typeStringOrg == null) { 2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa continue; 2054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 20600b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa final String typeStringUpperCase = typeStringOrg.toUpperCase(); 20700b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_PREF)) { 2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa hasPref = true; 20900b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_FAX)) { 2104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa isFax = true; 2114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 21200b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa final String labelCandidate; 21300b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa if (typeStringUpperCase.startsWith("X-") && type < 0) { 21400b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa labelCandidate = typeStringOrg.substring(2); 21500b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa } else { 21600b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa labelCandidate = typeStringOrg; 2174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 21800b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa if (labelCandidate.length() == 0) { 2194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa continue; 2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 22100b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa // e.g. "home" -> TYPE_HOME 22200b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa final Integer tmp = sKnownPhoneTypeMap_StoI.get(labelCandidate.toUpperCase()); 2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (tmp != null) { 2244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int typeCandidate = tmp; 225449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // 1. If a type isn't specified yet, we'll choose the new type candidate. 226449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // 2. If the current type is default one (OTHER) or custom one, we'll 227449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // prefer more specific types specified in the vCard. Note that OTHER and 228449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // the other different types may appear simultaneously here, since vCard 229449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // allow to have VOICE and HOME/WORK in one line. 230449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // e.g. "TEL;WORK;VOICE:1" -> WORK + OTHER -> Type should be WORK 231449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa // 3. TYPE_PAGER is prefered when the number contains @ surronded by 2324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // a pager number and a domain name. 2334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // e.g. 2344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // o 1111@domain.com 2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // x @domain.com 2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // x 1111@ 2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int indexOfAt = number.indexOf("@"); 2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if ((typeCandidate == Phone.TYPE_PAGER 2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && 0 < indexOfAt && indexOfAt < number.length() - 1) 2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa || type < 0 241449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa || type == Phone.TYPE_CUSTOM 242449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa || type == Phone.TYPE_OTHER) { 2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa type = tmp; 2444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (type < 0) { 2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa type = Phone.TYPE_CUSTOM; 24700b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa label = labelCandidate; 2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (type < 0) { 2534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (hasPref) { 2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa type = Phone.TYPE_MAIN; 2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // default to TYPE_HOME 2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa type = Phone.TYPE_HOME; 2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (isFax) { 2614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (type == Phone.TYPE_HOME) { 2624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa type = Phone.TYPE_FAX_HOME; 2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (type == Phone.TYPE_WORK) { 2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa type = Phone.TYPE_FAX_WORK; 2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (type == Phone.TYPE_OTHER) { 2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa type = Phone.TYPE_OTHER_FAX; 2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (type == Phone.TYPE_CUSTOM) { 2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return label; 2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return type; 2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa @SuppressWarnings("deprecation") 2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean isMobilePhoneLabel(final String label) { 2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // For backward compatibility. 2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now. 2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // To support mobile type at that time, this custom label had been used. 2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label)); 2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) { 2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return sPhoneTypesUnknownToContactsSet.contains(label); 2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static String getPropertyNameForIm(final int protocol) { 2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return sKnownImPropNameMap_ItoS.get(protocol); 2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2921de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public static String[] sortNameElements(final int nameOrder, 2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String familyName, final String middleName, final String givenName) { 2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String[] list = new String[3]; 2951de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final int nameOrderType = VCardConfig.getNameOrderType(nameOrder); 2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa switch (nameOrderType) { 2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case VCardConfig.NAME_ORDER_JAPANESE: { 2984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (containsOnlyPrintableAscii(familyName) && 2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa containsOnlyPrintableAscii(givenName)) { 3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[0] = givenName; 3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[1] = middleName; 3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[2] = familyName; 3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[0] = familyName; 3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[1] = middleName; 3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[2] = givenName; 3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa case VCardConfig.NAME_ORDER_EUROPE: { 3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[0] = middleName; 3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[1] = givenName; 3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[2] = familyName; 3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa default: { 3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[0] = givenName; 3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[1] = middleName; 3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list[2] = familyName; 3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return list; 3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static int getPhoneNumberFormat(final int vcardType) { 3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (VCardConfig.isJapaneseDevice(vcardType)) { 3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return PhoneNumberUtils.FORMAT_JAPAN; 3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return PhoneNumberUtils.FORMAT_NANP; 3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3341de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public static String constructNameFromElements(final int nameOrder, 3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String familyName, final String middleName, final String givenName) { 3361de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa return constructNameFromElements(nameOrder, familyName, middleName, givenName, 3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa null, null); 3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3401de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa public static String constructNameFromElements(final int nameOrder, 3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String familyName, final String middleName, final String givenName, 3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String prefix, final String suffix) { 3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final StringBuilder builder = new StringBuilder(); 3441de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa final String[] nameList = sortNameElements(nameOrder, familyName, middleName, givenName); 3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa boolean first = true; 3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!TextUtils.isEmpty(prefix)) { 3474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa first = false; 3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(prefix); 3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (final String namePart : nameList) { 3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!TextUtils.isEmpty(namePart)) { 3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (first) { 3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa first = false; 3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(' '); 3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(namePart); 3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!TextUtils.isEmpty(suffix)) { 3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!first) { 3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(' '); 3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(suffix); 3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3694560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa /** 3704560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa * Splits the given value into pieces using the delimiter ';' inside it. 3714560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa * 3724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa * Escaped characters in those values are automatically unescaped into original form. 3734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa */ 3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static List<String> constructListFromValue(final String value, 3754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa final int vcardType) { 3764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final List<String> list = new ArrayList<String>(); 3774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa StringBuilder builder = new StringBuilder(); 3784560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa final int length = value.length(); 3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa char ch = value.charAt(i); 3814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '\\' && i < length - 1) { 3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa char nextCh = value.charAt(i + 1); 3834560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa final String unescapedString; 3844560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa if (VCardConfig.isVersion40(vcardType)) { 3854560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa unescapedString = VCardParserImpl_V40.unescapeCharacter(nextCh); 3864560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } else if (VCardConfig.isVersion30(vcardType)) { 3874560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa unescapedString = VCardParserImpl_V30.unescapeCharacter(nextCh); 3884560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } else { 3894560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa if (!VCardConfig.isVersion21(vcardType)) { 3904560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa // Unknown vCard type 3914560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa Log.w(LOG_TAG, "Unknown vCard type"); 3924560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 3934560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa unescapedString = VCardParserImpl_V21.unescapeCharacter(nextCh); 3944560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa } 3954560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa 3964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (unescapedString != null) { 3974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(unescapedString); 3984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa i++; 3994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(ch); 4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == ';') { 4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list.add(builder.toString()); 4044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder = new StringBuilder(); 4054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 4064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(ch); 4074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list.add(builder.toString()); 4104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return list; 4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean containsOnlyPrintableAscii(final String...values) { 4144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (values == null) { 4154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return containsOnlyPrintableAscii(Arrays.asList(values)); 4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean containsOnlyPrintableAscii(final Collection<String> values) { 4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (values == null) { 4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (final String value : values) { 4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (TextUtils.isEmpty(value)) { 4264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa continue; 4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4289919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa if (!TextUtilsPort.isPrintableAsciiOnly(value)) { 4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 4304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 4364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 4374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * This is useful when checking the string should be encoded into quoted-printable 4384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * or not, which is required by vCard 2.1. 4394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 4404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 4414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the definition of "7bit" in vCard 2.1 spec for more information. 4424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 4434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) { 4454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (values == null) { 4464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values)); 4494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean containsOnlyNonCrLfPrintableAscii(final Collection<String> values) { 4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (values == null) { 4534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int asciiFirst = 0x20; 4564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int asciiLast = 0x7E; // included 4574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (final String value : values) { 4584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (TextUtils.isEmpty(value)) { 4594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa continue; 4604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = value.length(); 4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { 4634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int c = value.codePointAt(i); 4644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(asciiFirst <= c && c <= asciiLast)) { 4654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 4664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final Set<Character> sUnAcceptableAsciiInV21WordSet = 4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' ')); 4744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 4764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 4774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * This is useful since vCard 3.0 often requires the ("X-") properties and groups 4784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * should contain only alphabets, digits, and hyphen. 4794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 48002117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa * <p> 4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Note: It is already known some devices (wrongly) outputs properties with characters 4824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * which should not be in the field. One example is "X-GOOGLE TALK". We accept 4834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * such kind of input but must never output it unless the target is very specific 4844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * to the device which is able to parse the malformed input. 4854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean containsOnlyAlphaDigitHyphen(final String...values) { 4884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (values == null) { 4894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return containsOnlyAlphaDigitHyphen(Arrays.asList(values)); 4924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 4944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean containsOnlyAlphaDigitHyphen(final Collection<String> values) { 4954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (values == null) { 4964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 4974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int upperAlphabetFirst = 0x41; // A 4994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int upperAlphabetAfterLast = 0x5b; // [ 5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int lowerAlphabetFirst = 0x61; // a 5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int lowerAlphabetAfterLast = 0x7b; // { 5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int digitFirst = 0x30; // 0 5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int digitAfterLast = 0x3A; // : 5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int hyphen = '-'; 5054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (final String str : values) { 5064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (TextUtils.isEmpty(str)) { 5074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa continue; 5084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = str.length(); 5104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { 5114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int codepoint = str.codePointAt(i); 5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) || 5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) || 5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa (digitFirst <= codepoint && codepoint < digitAfterLast) || 5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa (codepoint == hyphen))) { 5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5232bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa public static boolean containsOnlyWhiteSpaces(final String...values) { 5242bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa if (values == null) { 5252bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa return true; 5262bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5272bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa return containsOnlyWhiteSpaces(Arrays.asList(values)); 5282bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5292bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa 5302bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa public static boolean containsOnlyWhiteSpaces(final Collection<String> values) { 5312bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa if (values == null) { 5322bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa return true; 5332bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5342bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa for (final String str : values) { 5352bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa if (TextUtils.isEmpty(str)) { 5362bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa continue; 5372bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5382bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa final int length = str.length(); 5392bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { 5402bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa if (!Character.isWhitespace(str.codePointAt(i))) { 5412bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa return false; 5422bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5432bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5442bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5452bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa return true; 5462bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 5472bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa 5484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 5494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 5504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Returns true when the given String is categorized as "word" specified in vCard spec 2.1. 5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 5524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * <p> 5534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * vCard 2.1 specifies:<br /> 5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * word = <any printable 7bit us-ascii except []=:., > 5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * </p> 5564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 5574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean isV21Word(final String value) { 5584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (TextUtils.isEmpty(value)) { 5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int asciiFirst = 0x20; 5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int asciiLast = 0x7E; // included 5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = value.length(); 5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { 5654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int c = value.codePointAt(i); 5664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!(asciiFirst <= c && c <= asciiLast) || 5674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sUnAcceptableAsciiInV21WordSet.contains((char)c)) { 5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 5724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 5734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5743d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa private static final int[] sEscapeIndicatorsV30 = new int[]{ 5753d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa ':', ';', ',', ' ' 5763d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa }; 5773d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa 5783d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa private static final int[] sEscapeIndicatorsV40 = new int[]{ 5793d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa ';', ':' 5803d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa }; 5813d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa 5822bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa /** 5832bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * <P> 5842bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * Returns String available as parameter value in vCard 3.0. 5852bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * </P> 5862bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * <P> 5872bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * RFC 2426 requires vCard composer to quote parameter values when it contains 5882bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * semi-colon, for example (See RFC 2426 for more information). 5892bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * This method checks whether the given String can be used without quotes. 5902bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * </P> 5912bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * <P> 5923d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa * Note: We remove DQUOTE inside the given value silently for now. 5932bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa * </P> 5942bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa */ 5953d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa public static String toStringAsV30ParamValue(String value) { 5963d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa return toStringAsParamValue(value, sEscapeIndicatorsV30); 5973d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa } 5983d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa 5993d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa public static String toStringAsV40ParamValue(String value) { 6003d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa return toStringAsParamValue(value, sEscapeIndicatorsV40); 6013d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa } 6023d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa 6033d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa private static String toStringAsParamValue(String value, final int[] escapeIndicators) { 6042bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa if (TextUtils.isEmpty(value)) { 6052bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa value = ""; 6062bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 6072bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa final int asciiFirst = 0x20; 6082bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa final int asciiLast = 0x7E; // included 6092bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa final StringBuilder builder = new StringBuilder(); 6102bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa final int length = value.length(); 6112bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa boolean needQuote = false; 6122bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { 6132bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa final int codePoint = value.codePointAt(i); 6142bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa if (codePoint < asciiFirst || codePoint == '"') { 6152bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa // CTL characters and DQUOTE are never accepted. Remove them. 6162bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa continue; 6172bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 6182bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa builder.appendCodePoint(codePoint); 6193d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa for (int indicator : escapeIndicators) { 6203d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa if (codePoint == indicator) { 6213d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa needQuote = true; 6223d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa break; 6233d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa } 6242bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 6252bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 6262bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa 6272bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa final String result = builder.toString(); 6282bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa return ((result.isEmpty() || VCardUtils.containsOnlyWhiteSpaces(result)) 6292bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa ? "" 6302bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa : (needQuote ? ('"' + result + '"') 6312bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa : result)); 6322bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa } 6332bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa 6344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static String toHalfWidthString(final String orgString) { 6354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (TextUtils.isEmpty(orgString)) { 6364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return null; 6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final StringBuilder builder = new StringBuilder(); 6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = orgString.length(); 6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) { 6414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // All Japanese character is able to be expressed by char. 6424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Do not need to use String#codepPointAt(). 6434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final char ch = orgString.charAt(i); 6444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch); 6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (halfWidthText != null) { 6464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(halfWidthText); 6474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 6484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(ch); 6494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return builder.toString(); 6524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Guesses the format of input image. Currently just the first few bytes are used. 6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when 6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * the guess failed. 6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @param input Image as byte array. 6594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return The image type or null when the type cannot be determined. 6604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 6614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static String guessImageType(final byte[] input) { 6624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (input == null) { 6634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return null; 6644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') { 6664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return "GIF"; 6674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (input.length >= 4 && input[0] == (byte) 0x89 6684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && input[1] == 'P' && input[2] == 'N' && input[3] == 'G') { 6694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Note: vCard 2.1 officially does not support PNG, but we may have it and 6704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // using X- word like "X-PNG" may not let importers know it is PNG. 6714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // So we use the String "PNG" as is... 6724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return "PNG"; 6734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (input.length >= 2 && input[0] == (byte) 0xff 6744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa && input[1] == (byte) 0xd8) { 6754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return "JPEG"; 6764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 6774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return null; 6784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 6824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * @return True when all the given values are null or empty Strings. 6834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 6844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public static boolean areAllEmpty(final String...values) { 6854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (values == null) { 6864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 6874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 6894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (final String value : values) { 6904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!TextUtils.isEmpty(value)) { 6914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return false; 6924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return true; 6954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 6964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 697d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng /** 698d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng * Checks to see if a string looks like it could be an android generated quoted printable. 699d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng * 700d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng * Identification of quoted printable is not 100% reliable since it's just ascii. But given 701d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng * the high number and exact location of generated = signs, there is a high likely-hood that 702d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng * it would be. 703d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng * 704d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng * @return True if it appears like android quoted printable. False otherwise. 705d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng */ 706d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng public static boolean appearsLikeAndroidVCardQuotedPrintable(String value) { 707d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng 708d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng // Quoted printable is always in multiple of 3s. With optional 1 '=' at end. 709d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng final int remainder = (value.length() % 3); 710d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng if (value.length() < 2 || (remainder != 1 && remainder != 0)) { 711d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng return false; 712d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng } 713d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng for (int i = 0; i < value.length(); i += 3) { 714d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng if (value.charAt(i) != '=') { 715d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng return false; 716d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng } 717d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng } 718d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng return true; 719d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng } 720d98c0fe9ab6a89129c31c510ccd629a2dca148afChiao Cheng 7214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa //// The methods bellow may be used by unit test. 7224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** 72458610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa * Unquotes given Quoted-Printable value. value must not be null. 7254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 72658610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa public static String parseQuotedPrintable( 72758610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa final String value, boolean strictLineBreaking, 7284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String sourceCharset, String targetCharset) { 7294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // "= " -> " ", "=\t" -> "\t". 7304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Previous code had done this replacement. Keep on the safe side. 7314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String quotedPrintable; 7324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa { 7334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final StringBuilder builder = new StringBuilder(); 7344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = value.length(); 7354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 7364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa char ch = value.charAt(i); 7374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '=' && i < length - 1) { 7384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa char nextCh = value.charAt(i + 1); 7394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (nextCh == ' ' || nextCh == '\t') { 7404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(nextCh); 7414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa i++; 7424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa continue; 7434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(ch); 7464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa quotedPrintable = builder.toString(); 7484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa String[] lines; 7514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (strictLineBreaking) { 7524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa lines = quotedPrintable.split("\r\n"); 7534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 7544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa StringBuilder builder = new StringBuilder(); 7554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int length = quotedPrintable.length(); 7564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa ArrayList<String> list = new ArrayList<String>(); 7574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int i = 0; i < length; i++) { 7584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa char ch = quotedPrintable.charAt(i); 7594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (ch == '\n') { 7604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list.add(builder.toString()); 7614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder = new StringBuilder(); 7624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (ch == '\r') { 7634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list.add(builder.toString()); 7644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder = new StringBuilder(); 7654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (i < length - 1) { 7664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa char nextCh = quotedPrintable.charAt(i + 1); 7674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (nextCh == '\n') { 7684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa i++; 7694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 7724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(ch); 7734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String lastLine = builder.toString(); 7764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (lastLine.length() > 0) { 7774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa list.add(lastLine); 7784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa lines = list.toArray(new String[0]); 7804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final StringBuilder builder = new StringBuilder(); 7834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (String line : lines) { 7844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (line.endsWith("=")) { 7854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa line = line.substring(0, line.length() - 1); 7864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa builder.append(line); 7884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String rawString = builder.toString(); 7914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (TextUtils.isEmpty(rawString)) { 7924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Given raw string is empty."); 7934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 7944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 7954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa byte[] rawBytes = null; 7964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 79702117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa rawBytes = rawString.getBytes(sourceCharset); 7984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (UnsupportedEncodingException e) { 7994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.w(LOG_TAG, "Failed to decode: " + sourceCharset); 8004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa rawBytes = rawString.getBytes(); 8014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa byte[] decodedBytes = null; 8044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 8052c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa decodedBytes = QuotedPrintableCodecPort.decodeQuotedPrintable(rawBytes); 8064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (DecoderException e) { 8074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.e(LOG_TAG, "DecoderException is thrown."); 8084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa decodedBytes = rawBytes; 8094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 8114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 8124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return new String(decodedBytes, targetCharset); 8134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (UnsupportedEncodingException e) { 8144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); 8154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return new String(decodedBytes); 8164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 819be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa public static final VCardParser getAppropriateParser(int vcardType) 820be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa throws VCardException { 821be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (VCardConfig.isVersion21(vcardType)) { 822be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return new VCardParser_V21(); 823be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } else if (VCardConfig.isVersion30(vcardType)) { 824be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return new VCardParser_V30(); 825be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } else if (VCardConfig.isVersion40(vcardType)) { 826be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return new VCardParser_V40(); 827be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } else { 828be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa throw new VCardException("Version is not specified"); 829be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 830be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 831be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa 83258ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa public static final String convertStringCharset( 83358ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa String originalString, String sourceCharset, String targetCharset) { 83458ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa if (sourceCharset.equalsIgnoreCase(targetCharset)) { 83558ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa return originalString; 83658ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa } 83758ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa final Charset charset = Charset.forName(sourceCharset); 83858ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa final ByteBuffer byteBuffer = charset.encode(originalString); 83958ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa // byteBuffer.array() "may" return byte array which is larger than 84058ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa // byteBuffer.remaining(). Here, we keep on the safe side. 84158ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa final byte[] bytes = new byte[byteBuffer.remaining()]; 84258ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa byteBuffer.get(bytes); 84358ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa try { 84458ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa return new String(bytes, targetCharset); 84558ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa } catch (UnsupportedEncodingException e) { 84658ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); 84758ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa return null; 84858ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa } 84958ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa } 85058ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa 851be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa // TODO: utilities for vCard 4.0: datetime, timestamp, integer, float, and boolean 852be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa 8534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private VCardUtils() { 8544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 8554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa} 856