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
18be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawaimport com.android.vcard.exception.VCardException;
19be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.content.ContentProviderOperation;
214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.Data;
224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Im;
234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Phone;
244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.telephony.PhoneNumberUtils;
269919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawaimport android.text.SpannableStringBuilder;
274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.text.TextUtils;
284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport android.util.Log;
294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
302c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawaimport java.io.ByteArrayOutputStream;
314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.UnsupportedEncodingException;
3258ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawaimport java.nio.ByteBuffer;
3358ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawaimport java.nio.charset.Charset;
344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList;
354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Arrays;
364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Collection;
374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashMap;
384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.HashSet;
394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.List;
404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Map;
414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Set;
424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/**
444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Utilities for VCard handling codes.
454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */
464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapublic class VCardUtils {
4702117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa    private static final String LOG_TAG = VCardConstants.LOG_TAG;
484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
492c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa    /**
502c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa     * See org.apache.commons.codec.DecoderException
512c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa     */
522c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa    private static class DecoderException extends Exception {
532c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa        public DecoderException(String pMessage) {
542c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            super(pMessage);
552c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa        }
562c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa    }
572c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa
582c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa    /**
592c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa     * See org.apache.commons.codec.net.QuotedPrintableCodec
602c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa     */
612c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa    private static class QuotedPrintableCodecPort {
622c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa        private static byte ESCAPE_CHAR = '=';
632c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa        public static final byte[] decodeQuotedPrintable(byte[] bytes)
642c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                throws DecoderException {
652c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            if (bytes == null) {
662c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                return null;
672c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            }
682c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
692c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            for (int i = 0; i < bytes.length; i++) {
702c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                int b = bytes[i];
712c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                if (b == ESCAPE_CHAR) {
722c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                    try {
732c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                        int u = Character.digit((char) bytes[++i], 16);
742c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                        int l = Character.digit((char) bytes[++i], 16);
752c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                        if (u == -1 || l == -1) {
762c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                            throw new DecoderException("Invalid quoted-printable encoding");
772c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                        }
782c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                        buffer.write((char) ((u << 4) + l));
792c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                    } catch (ArrayIndexOutOfBoundsException e) {
802c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                        throw new DecoderException("Invalid quoted-printable encoding");
812c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                    }
822c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                } else {
832c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                    buffer.write(b);
842c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa                }
852c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            }
862c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            return buffer.toByteArray();
872c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa        }
882c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa    }
892c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa
909919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa    /**
919919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa     * Ported methods which are hidden in {@link PhoneNumberUtils}.
929919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa     */
939919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa    public static class PhoneNumberUtilsPort {
949919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa        public static String formatNumber(String source, int defaultFormattingType) {
959919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            final SpannableStringBuilder text = new SpannableStringBuilder(source);
969919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            PhoneNumberUtils.formatNumber(text, defaultFormattingType);
979919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            return text.toString();
989919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa        }
999919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa    }
1009919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa
1019919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa    /**
1029919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa     * Ported methods which are hidden in {@link TextUtils}.
1039919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa     */
1049919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa    public static class TextUtilsPort {
1059919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa        public static boolean isPrintableAscii(final char c) {
1069919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            final int asciiFirst = 0x20;
1079919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            final int asciiLast = 0x7E;  // included
1089919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            return (asciiFirst <= c && c <= asciiLast) || c == '\r' || c == '\n';
1099919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa        }
1109919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa
1119919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa        public static boolean isPrintableAsciiOnly(final CharSequence str) {
1129919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            final int len = str.length();
1139919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            for (int i = 0; i < len; i++) {
1149919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa                if (!isPrintableAscii(str.charAt(i))) {
1159919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa                    return false;
1169919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa                }
1179919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            }
1189919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            return true;
1199919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa        }
1209919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa    }
1219919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa
1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
1234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    // converted to two parameter Strings. These only contain some minor fields valid in both
12402117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa    // vCard and current (as of 2009-08-07) Contacts structure.
1254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Set<String> sPhoneTypesUnknownToContactsSet;
1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Map<String, Integer> sKnownPhoneTypeMap_StoI;
1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Map<Integer, String> sKnownImPropNameMap_ItoS;
1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Set<String> sMobilePhoneLabelSet;
1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    static {
1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>();
1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR);
1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR);
1374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER);
1384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER);
1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN);
1404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN);
14102117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa
1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME);
1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK);
1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE);
14502117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa
1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER);
1474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK,
1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Phone.TYPE_CALLBACK);
1494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(
1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO);
1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD,
1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Phone.TYPE_TTY_TDD);
1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT,
1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                Phone.TYPE_ASSISTANT);
156449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa        // OTHER (default in Android) should correspond to VOICE (default in vCard).
157449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_VOICE, Phone.TYPE_OTHER);
1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet = new HashSet<String>();
1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM);
1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG);
1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS);
1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO);
1644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS = new HashMap<Integer, String>();
1664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM);
1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN);
1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO);
1694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK,
1714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                VCardConstants.PROPERTY_X_GOOGLE_TALK);
1724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
1734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
1744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ);
1754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING);
1764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone)
1784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone)
1794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone)
1804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone)
1814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        sMobilePhoneLabelSet = new HashSet<String>(Arrays.asList(
1824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                "MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4",
1834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                "\uFF79\uFF72\uFF80\uFF72"));
1844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String getPhoneTypeString(Integer type) {
1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return sKnownPhoneTypesMap_ItoS.get(type);
1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns Interger when the given types can be parsed as known type. Returns String object
19202117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa     * when not, which should be set to label.
1934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static Object getPhoneTypeFromStrings(Collection<String> types,
1954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String number) {
1964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (number == null) {
1974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            number = "";
1984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
1994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        int type = -1;
2004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String label = null;
2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean isFax = false;
2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean hasPref = false;
20302117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa
2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (types != null) {
20500b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa            for (final String typeStringOrg : types) {
20600b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                if (typeStringOrg == null) {
2074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    continue;
2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
20900b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                final String typeStringUpperCase = typeStringOrg.toUpperCase();
21000b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_PREF)) {
2114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    hasPref = true;
21200b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                } else if (typeStringUpperCase.equals(VCardConstants.PARAM_TYPE_FAX)) {
2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    isFax = true;
2144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
21500b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                    final String labelCandidate;
21600b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                    if (typeStringUpperCase.startsWith("X-") && type < 0) {
21700b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                        labelCandidate = typeStringOrg.substring(2);
21800b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                    } else {
21900b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                        labelCandidate = typeStringOrg;
2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
22100b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                    if (labelCandidate.length() == 0) {
2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        continue;
2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
22400b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                    // e.g. "home" -> TYPE_HOME
22500b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                    final Integer tmp = sKnownPhoneTypeMap_StoI.get(labelCandidate.toUpperCase());
2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (tmp != null) {
2274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final int typeCandidate = tmp;
228449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                        // 1. If a type isn't specified yet, we'll choose the new type candidate.
229449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                        // 2. If the current type is default one (OTHER) or custom one, we'll
230449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                        // prefer more specific types specified in the vCard. Note that OTHER and
231449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                        // the other different types may appear simultaneously here, since vCard
232449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                        // allow to have VOICE and HOME/WORK in one line.
233449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                        // e.g. "TEL;WORK;VOICE:1" -> WORK + OTHER -> Type should be WORK
234449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                        // 3. TYPE_PAGER is prefered when the number contains @ surronded by
2354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // a pager number and a domain name.
2364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // e.g.
2374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // o 1111@domain.com
2384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // x @domain.com
2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        // x 1111@
2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        final int indexOfAt = number.indexOf("@");
2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if ((typeCandidate == Phone.TYPE_PAGER
2424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                && 0 < indexOfAt && indexOfAt < number.length() - 1)
2434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                                || type < 0
244449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                                || type == Phone.TYPE_CUSTOM
245449d710428682b3f44ba20ce290564cd9352ca0aDaisuke Miyakawa                                || type == Phone.TYPE_OTHER) {
2464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            type = tmp;
2474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
2484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    } else if (type < 0) {
2494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        type = Phone.TYPE_CUSTOM;
25000b4b98ea94df7fa3f88ee9a623d60db0d4fc451Daisuke Miyakawa                        label = labelCandidate;
2514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
2524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
2534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (type < 0) {
2564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (hasPref) {
2574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_MAIN;
2584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
2594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                // default to TYPE_HOME
2604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_HOME;
2614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (isFax) {
2644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (type == Phone.TYPE_HOME) {
2654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_FAX_HOME;
2664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (type == Phone.TYPE_WORK) {
2674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_FAX_WORK;
2684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (type == Phone.TYPE_OTHER) {
2694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                type = Phone.TYPE_OTHER_FAX;
2704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (type == Phone.TYPE_CUSTOM) {
2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return label;
2744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return type;
2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    @SuppressWarnings("deprecation")
2804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean isMobilePhoneLabel(final String label) {
2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // For backward compatibility.
2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        //         To support mobile type at that time, this custom label had been used.
2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label));
2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) {
2884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return sPhoneTypesUnknownToContactsSet.contains(label);
2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String getPropertyNameForIm(final int protocol) {
2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return sKnownImPropNameMap_ItoS.get(protocol);
2934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
2951de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public static String[] sortNameElements(final int nameOrder,
2964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String familyName, final String middleName, final String givenName) {
2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String[] list = new String[3];
2981de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final int nameOrderType = VCardConfig.getNameOrderType(nameOrder);
2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        switch (nameOrderType) {
3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case VCardConfig.NAME_ORDER_JAPANESE: {
3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (containsOnlyPrintableAscii(familyName) &&
3024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        containsOnlyPrintableAscii(givenName)) {
3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[0] = givenName;
3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[1] = middleName;
3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[2] = familyName;
3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[0] = familyName;
3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[1] = middleName;
3094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list[2] = givenName;
3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            case VCardConfig.NAME_ORDER_EUROPE: {
3144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[0] = middleName;
3154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[1] = givenName;
3164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[2] = familyName;
3174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            default: {
3204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[0] = givenName;
3214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[1] = middleName;
3224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list[2] = familyName;
3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                break;
3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return list;
3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static int getPhoneNumberFormat(final int vcardType) {
3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (VCardConfig.isJapaneseDevice(vcardType)) {
3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return PhoneNumberUtils.FORMAT_JAPAN;
3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return PhoneNumberUtils.FORMAT_NANP;
3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3371de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public static String constructNameFromElements(final int nameOrder,
3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String familyName, final String middleName, final String givenName) {
3391de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        return constructNameFromElements(nameOrder, familyName, middleName, givenName,
3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                null, null);
3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3431de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa    public static String constructNameFromElements(final int nameOrder,
3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String familyName, final String middleName, final String givenName,
3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String prefix, final String suffix) {
3464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
3471de396f6df89363169d3a2e61a61fa98d12c1ef8Daisuke Miyakawa        final String[] nameList = sortNameElements(nameOrder, familyName, middleName, givenName);
3484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        boolean first = true;
3494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!TextUtils.isEmpty(prefix)) {
3504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            first = false;
3514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(prefix);
3524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String namePart : nameList) {
3544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!TextUtils.isEmpty(namePart)) {
3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (first) {
3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    first = false;
3574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(' ');
3594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(namePart);
3614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (!TextUtils.isEmpty(suffix)) {
3644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!first) {
3654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(' ');
3664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
3674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(suffix);
3684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
3694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
3724560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa    /**
3734560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * Splits the given value into pieces using the delimiter ';' inside it.
3744560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     *
3754560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     * Escaped characters in those values are automatically unescaped into original form.
3764560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa     */
3774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static List<String> constructListFromValue(final String value,
3784560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa            final int vcardType) {
3794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final List<String> list = new ArrayList<String>();
3804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        StringBuilder builder = new StringBuilder();
3814560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa        final int length = value.length();
3824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i++) {
3834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            char ch = value.charAt(i);
3844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (ch == '\\' && i < length - 1) {
3854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                char nextCh = value.charAt(i + 1);
3864560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                final String unescapedString;
3874560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                if (VCardConfig.isVersion40(vcardType)) {
3884560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    unescapedString = VCardParserImpl_V40.unescapeCharacter(nextCh);
3894560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                } else if (VCardConfig.isVersion30(vcardType)) {
3904560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    unescapedString = VCardParserImpl_V30.unescapeCharacter(nextCh);
3914560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                } else {
3924560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    if (!VCardConfig.isVersion21(vcardType)) {
3934560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        // Unknown vCard type
3944560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                        Log.w(LOG_TAG, "Unknown vCard type");
3954560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    }
3964560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                    unescapedString = VCardParserImpl_V21.unescapeCharacter(nextCh);
3974560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa                }
3984560bdde6dd75cca49fc55b58aafb5d416b88ca3Daisuke Miyakawa
3994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (unescapedString != null) {
4004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(unescapedString);
4014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    i++;
4024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
4034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(ch);
4044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else if (ch == ';') {
4064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list.add(builder.toString());
4074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder = new StringBuilder();
4084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
4094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
4104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        list.add(builder.toString());
4134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return list;
4144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyPrintableAscii(final String...values) {
4174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return containsOnlyPrintableAscii(Arrays.asList(values));
4214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyPrintableAscii(final Collection<String> values) {
4244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String value : values) {
4284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(value)) {
4294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                continue;
4304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4319919ad2126c06dbf2eb54a11e6158f87f316bc22Daisuke Miyakawa            if (!TextUtilsPort.isPrintableAsciiOnly(value)) {
4324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
4334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
4364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
4394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
4404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * This is useful when checking the string should be encoded into quoted-printable
4414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * or not, which is required by vCard 2.1.
4424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
4434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
4444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * See the definition of "7bit" in vCard 2.1 spec for more information.
4454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
4464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) {
4484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values));
4524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyNonCrLfPrintableAscii(final Collection<String> values) {
4554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiFirst = 0x20;
4594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiLast = 0x7E;  // included
4604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String value : values) {
4614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(value)) {
4624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                continue;
4634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = value.length();
4654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
4664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                final int c = value.codePointAt(i);
4674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (!(asciiFirst <= c && c <= asciiLast)) {
4684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
4694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
4704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
4714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
4734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private static final Set<Character> sUnAcceptableAsciiInV21WordSet =
4764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
4774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
4794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
4804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * This is useful since vCard 3.0 often requires the ("X-") properties and groups
4814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * should contain only alphabets, digits, and hyphen.
4824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
48302117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa     * <p>
4844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Note: It is already known some devices (wrongly) outputs properties with characters
4854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *       which should not be in the field. One example is "X-GOOGLE TALK". We accept
4864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *       such kind of input but must never output it unless the target is very specific
4874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     *       to the device which is able to parse the malformed input.
4884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
4894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
4904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyAlphaDigitHyphen(final String...values) {
4914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
4934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
4944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return containsOnlyAlphaDigitHyphen(Arrays.asList(values));
4954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
4964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
4974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean containsOnlyAlphaDigitHyphen(final Collection<String> values) {
4984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
4994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
5004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int upperAlphabetFirst = 0x41;  // A
5024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int upperAlphabetAfterLast = 0x5b;  // [
5034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int lowerAlphabetFirst = 0x61;  // a
5044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int lowerAlphabetAfterLast = 0x7b;  // {
5054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int digitFirst = 0x30;  // 0
5064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int digitAfterLast = 0x3A;  // :
5074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int hyphen = '-';
5084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String str : values) {
5094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (TextUtils.isEmpty(str)) {
5104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                continue;
5114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = str.length();
5134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
5144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                int codepoint = str.codePointAt(i);
5154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) ||
5164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) ||
5174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    (digitFirst <= codepoint && codepoint < digitAfterLast) ||
5184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    (codepoint == hyphen))) {
5194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    return false;
5204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
5214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
5244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5262bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa    public static boolean containsOnlyWhiteSpaces(final String...values) {
5272bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        if (values == null) {
5282bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            return true;
5292bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        }
5302bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        return containsOnlyWhiteSpaces(Arrays.asList(values));
5312bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa    }
5322bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa
5332bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa    public static boolean containsOnlyWhiteSpaces(final Collection<String> values) {
5342bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        if (values == null) {
5352bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            return true;
5362bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        }
5372bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        for (final String str : values) {
5382bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            if (TextUtils.isEmpty(str)) {
5392bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                continue;
5402bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            }
5412bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            final int length = str.length();
5422bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
5432bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                if (!Character.isWhitespace(str.codePointAt(i))) {
5442bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                    return false;
5452bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                }
5462bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            }
5472bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        }
5482bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        return true;
5492bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa    }
5502bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa
5514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
5524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
5534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
5544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
5554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * <p>
5564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * vCard 2.1 specifies:<br />
5574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * word = &lt;any printable 7bit us-ascii except []=:., &gt;
5584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * </p>
5594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
5604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean isV21Word(final String value) {
5614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(value)) {
5624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
5634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiFirst = 0x20;
5654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int asciiLast = 0x7E;  // included
5664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = value.length();
5674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
5684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int c = value.codePointAt(i);
5694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!(asciiFirst <= c && c <= asciiLast) ||
5704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    sUnAcceptableAsciiInV21WordSet.contains((char)c)) {
5714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
5724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
5734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
5744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
5754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
5764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
5773d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    private static final int[] sEscapeIndicatorsV30 = new int[]{
5783d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa        ':', ';', ',', ' '
5793d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    };
5803d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa
5813d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    private static final int[] sEscapeIndicatorsV40 = new int[]{
5823d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa        ';', ':'
5833d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    };
5843d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa
5852bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa    /**
5862bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * <P>
5872bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * Returns String available as parameter value in vCard 3.0.
5882bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * </P>
5892bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * <P>
5902bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * RFC 2426 requires vCard composer to quote parameter values when it contains
5912bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * semi-colon, for example (See RFC 2426 for more information).
5922bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * This method checks whether the given String can be used without quotes.
5932bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * </P>
5942bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * <P>
5953d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa     * Note: We remove DQUOTE inside the given value silently for now.
5962bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     * </P>
5972bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa     */
5983d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    public static String toStringAsV30ParamValue(String value) {
5993d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa        return toStringAsParamValue(value, sEscapeIndicatorsV30);
6003d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    }
6013d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa
6023d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    public static String toStringAsV40ParamValue(String value) {
6033d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa        return toStringAsParamValue(value, sEscapeIndicatorsV40);
6043d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    }
6053d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa
6063d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa    private static String toStringAsParamValue(String value, final int[] escapeIndicators) {
6072bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        if (TextUtils.isEmpty(value)) {
6082bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            value = "";
6092bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        }
6102bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        final int asciiFirst = 0x20;
6112bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        final int asciiLast = 0x7E;  // included
6122bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
6132bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        final int length = value.length();
6142bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        boolean needQuote = false;
6152bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
6162bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            final int codePoint = value.codePointAt(i);
6172bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            if (codePoint < asciiFirst || codePoint == '"') {
6182bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                // CTL characters and DQUOTE are never accepted. Remove them.
6192bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                continue;
6202bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            }
6212bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            builder.appendCodePoint(codePoint);
6223d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa            for (int indicator : escapeIndicators) {
6233d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa                if (codePoint == indicator) {
6243d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa                    needQuote = true;
6253d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa                    break;
6263d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa                }
6272bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa            }
6282bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        }
6292bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa
6302bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        final String result = builder.toString();
6312bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa        return ((result.isEmpty() || VCardUtils.containsOnlyWhiteSpaces(result))
6322bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                ? ""
6332bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                : (needQuote ? ('"' + result + '"')
6342bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa                : result));
6352bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa    }
6362bf85a1a15a3175119ab8415fc590fd5fe3d0752Daisuke Miyakawa
6374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String toHalfWidthString(final String orgString) {
6384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(orgString)) {
6394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
6404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
6424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final int length = orgString.length();
6434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) {
6444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // All Japanese character is able to be expressed by char.
6454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Do not need to use String#codepPointAt().
6464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final char ch = orgString.charAt(i);
6474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
6484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (halfWidthText != null) {
6494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(halfWidthText);
6504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            } else {
6514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
6524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return builder.toString();
6554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * Guesses the format of input image. Currently just the first few bytes are used.
6594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when
6604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * the guess failed.
6614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @param input Image as byte array.
6624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return The image type or null when the type cannot be determined.
6634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static String guessImageType(final byte[] input) {
6654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (input == null) {
6664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
6674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') {
6694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "GIF";
6704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (input.length >= 4 && input[0] == (byte) 0x89
6714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && input[1] == 'P' && input[2] == 'N' && input[3] == 'G') {
6724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            // Note: vCard 2.1 officially does not support PNG, but we may have it and
6734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            //       using X- word like "X-PNG" may not let importers know it is PNG.
6744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            //       So we use the String "PNG" as is...
6754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "PNG";
6764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else if (input.length >= 2 && input[0] == (byte) 0xff
6774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                && input[1] == (byte) 0xd8) {
6784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return "JPEG";
6794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
6804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return null;
6814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
6854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     * @return True when all the given values are null or empty Strings.
6864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
6874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    public static boolean areAllEmpty(final String...values) {
6884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (values == null) {
6894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return true;
6904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
6924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (final String value : values) {
6934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (!TextUtils.isEmpty(value)) {
6944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                return false;
6954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
6964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
6974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        return true;
6984199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
6994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    //// The methods bellow may be used by unit test.
7014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    /**
70358610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa     * Unquotes given Quoted-Printable value. value must not be null.
7044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa     */
70558610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa    public static String parseQuotedPrintable(
70658610106ce61adad9b1caa1fe9f7925c3e938babDaisuke Miyakawa            final String value, boolean strictLineBreaking,
7074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            String sourceCharset, String targetCharset) {
7084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // "= " -> " ", "=\t" -> "\t".
7094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        // Previous code had done this replacement. Keep on the safe side.
7104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String quotedPrintable;
7114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        {
7124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final StringBuilder builder = new StringBuilder();
7134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = value.length();
7144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i++) {
7154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                char ch = value.charAt(i);
7164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (ch == '=' && i < length - 1) {
7174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    char nextCh = value.charAt(i + 1);
7184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (nextCh == ' ' || nextCh == '\t') {
7194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        builder.append(nextCh);
7204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        i++;
7214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        continue;
7224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
7234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
7244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                builder.append(ch);
7254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            quotedPrintable = builder.toString();
7274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        String[] lines;
7304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (strictLineBreaking) {
7314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            lines = quotedPrintable.split("\r\n");
7324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } else {
7334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            StringBuilder builder = new StringBuilder();
7344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final int length = quotedPrintable.length();
7354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            ArrayList<String> list = new ArrayList<String>();
7364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            for (int i = 0; i < length; i++) {
7374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                char ch = quotedPrintable.charAt(i);
7384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                if (ch == '\n') {
7394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list.add(builder.toString());
7404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder = new StringBuilder();
7414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else if (ch == '\r') {
7424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    list.add(builder.toString());
7434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder = new StringBuilder();
7444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    if (i < length - 1) {
7454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        char nextCh = quotedPrintable.charAt(i + 1);
7464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        if (nextCh == '\n') {
7474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                            i++;
7484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                        }
7494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    }
7504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                } else {
7514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                    builder.append(ch);
7524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                }
7534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            final String lastLine = builder.toString();
7554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (lastLine.length() > 0) {
7564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                list.add(lastLine);
7574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            lines = list.toArray(new String[0]);
7594199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final StringBuilder builder = new StringBuilder();
7624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        for (String line : lines) {
7634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            if (line.endsWith("=")) {
7644199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa                line = line.substring(0, line.length() - 1);
7654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            }
7664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            builder.append(line);
7674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        final String rawString = builder.toString();
7704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        if (TextUtils.isEmpty(rawString)) {
7714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "Given raw string is empty.");
7724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        byte[] rawBytes = null;
7754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
77602117b3d19787ff65486b9f9db8abd338ae4c9f9Daisuke Miyakawa            rawBytes = rawString.getBytes(sourceCharset);
7774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (UnsupportedEncodingException e) {
7784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.w(LOG_TAG, "Failed to decode: " + sourceCharset);
7794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            rawBytes = rawString.getBytes();
7804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        byte[] decodedBytes = null;
7834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
7842c4b5f3a9df3eff8ed8a7b3d2dc7739277e07d41Daisuke Miyakawa            decodedBytes = QuotedPrintableCodecPort.decodeQuotedPrintable(rawBytes);
7854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (DecoderException e) {
7864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.e(LOG_TAG, "DecoderException is thrown.");
7874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            decodedBytes = rawBytes;
7884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
7904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        try {
7914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return new String(decodedBytes, targetCharset);
7924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        } catch (UnsupportedEncodingException e) {
7934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
7944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa            return new String(decodedBytes);
7954199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa        }
7964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
7974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa
798be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    public static final VCardParser getAppropriateParser(int vcardType)
799be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            throws VCardException {
800be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        if (VCardConfig.isVersion21(vcardType)) {
801be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            return new VCardParser_V21();
802be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        } else if (VCardConfig.isVersion30(vcardType)) {
803be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            return new VCardParser_V30();
804be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        } else if (VCardConfig.isVersion40(vcardType)) {
805be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            return new VCardParser_V40();
806be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        } else {
807be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa            throw new VCardException("Version is not specified");
808be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa        }
809be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    }
810be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
81158ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa    public static final String convertStringCharset(
81258ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa            String originalString, String sourceCharset, String targetCharset) {
81358ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        if (sourceCharset.equalsIgnoreCase(targetCharset)) {
81458ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa            return originalString;
81558ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        }
81658ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        final Charset charset = Charset.forName(sourceCharset);
81758ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        final ByteBuffer byteBuffer = charset.encode(originalString);
81858ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        // byteBuffer.array() "may" return byte array which is larger than
81958ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        // byteBuffer.remaining(). Here, we keep on the safe side.
82058ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        final byte[] bytes = new byte[byteBuffer.remaining()];
82158ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        byteBuffer.get(bytes);
82258ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        try {
82358ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa            return new String(bytes, targetCharset);
82458ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        } catch (UnsupportedEncodingException e) {
82558ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
82658ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa            return null;
82758ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa        }
82858ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa    }
82958ca5f9943bb5c8aeeab3150ac96f1143dfd86baDaisuke Miyakawa
830be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa    // TODO: utilities for vCard 4.0: datetime, timestamp, integer, float, and boolean
831be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa
8324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    private VCardUtils() {
8334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa    }
8344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa}
835