diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java index 0a6415dd200cd..c2b342f673ac5 100644 --- a/core/java/android/pim/vcard/VCardBuilder.java +++ b/core/java/android/pim/vcard/VCardBuilder.java @@ -47,7 +47,22 @@ import java.util.Map; import java.util.Set; /** - * The class which lets users create their own vCard String. + *
+ * The class which lets users create their own vCard String. Typical usage is as follows: + *
+ *final VCardBuilder builder = new VCardBuilder(vcardType); + * builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE)) + * .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE)) + * .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE)) + * .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE)) + * .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE)) + * .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE)) + * .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE)) + * .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE)) + * .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE)) + * .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE)) + * .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE)) + * .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));*/ public class VCardBuilder { private static final String LOG_TAG = "VCardBuilder"; @@ -81,7 +96,6 @@ public class VCardBuilder { private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b"; private static final String SHIFT_JIS = "SHIFT_JIS"; - private static final String UTF_8 = "UTF-8"; private final int mVCardType; @@ -92,21 +106,28 @@ public class VCardBuilder { private final boolean mShouldUseQuotedPrintable; private final boolean mUsesAndroidProperty; private final boolean mUsesDefactProperty; - private final boolean mUsesUtf8; - private final boolean mUsesShiftJis; private final boolean mAppendTypeParamName; private final boolean mRefrainsQPToNameProperties; private final boolean mNeedsToConvertPhoneticString; private final boolean mShouldAppendCharsetParam; - private final String mCharsetString; + private final String mCharset; private final String mVCardCharsetParameter; private StringBuilder mBuilder; private boolean mEndAppended; public VCardBuilder(final int vcardType) { + // Default charset should be used + this(vcardType, null); + } + + /** + * @param vcardType + * @param charset If null, we use default charset for export. + */ + public VCardBuilder(final int vcardType, String charset) { mVCardType = vcardType; mIsV30 = VCardConfig.isV30(vcardType); @@ -116,40 +137,77 @@ public class VCardBuilder { mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType); mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType); mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType); - mUsesUtf8 = VCardConfig.usesUtf8(vcardType); - mUsesShiftJis = VCardConfig.usesShiftJis(vcardType); mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType); mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType); mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType); - mShouldAppendCharsetParam = !(mIsV30 && mUsesUtf8); + final boolean shouldUseUtf8 = VCardConfig.shouldUseUtf8ForExport(vcardType); + final boolean shouldUseShiftJis = VCardConfig.shouldUseShiftJisForExport(vcardType); - if (mIsDoCoMo) { - String charset; - try { - charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name(); - } catch (UnsupportedCharsetException e) { - Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is."); - charset = SHIFT_JIS; + // vCard 2.1 requires charset. + // vCard 3.0 does not allow it but we found some devices use it to determine + // the exact charset. + // We currently append it only when charset other than UTF_8 is used. + mShouldAppendCharsetParam = !(mIsV30 && shouldUseUtf8); + + if (VCardConfig.isDoCoMo(vcardType) || shouldUseShiftJis) { + if (!SHIFT_JIS.equalsIgnoreCase(charset)) { + Log.w(LOG_TAG, + "The charset \"" + charset + "\" is used while " + + SHIFT_JIS + " is needed to be used."); + if (TextUtils.isEmpty(charset)) { + mCharset = SHIFT_JIS; + } else { + try { + charset = CharsetUtils.charsetForVendor(charset).name(); + } catch (UnsupportedCharsetException e) { + Log.i(LOG_TAG, + "Career-specific \"" + charset + "\" was not found (as usual). " + + "Use it as is."); + } + mCharset = charset; + } + } else { + if (mIsDoCoMo) { + try { + charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name(); + } catch (UnsupportedCharsetException e) { + Log.e(LOG_TAG, + "DoCoMo-specific SHIFT_JIS was not found. " + + "Use SHIFT_JIS as is."); + charset = SHIFT_JIS; + } + } else { + try { + charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name(); + } catch (UnsupportedCharsetException e) { + Log.e(LOG_TAG, + "Career-specific SHIFT_JIS was not found. " + + "Use SHIFT_JIS as is."); + charset = SHIFT_JIS; + } + } + mCharset = charset; } - mCharsetString = charset; - // Do not use mCharsetString bellow since it is different from "SHIFT_JIS" but - // may be "DOCOMO_SHIFT_JIS" or something like that (internal expression used in - // Android, not shown to the public). - mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS; - } else if (mUsesShiftJis) { - String charset; - try { - charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name(); - } catch (UnsupportedCharsetException e) { - Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is."); - charset = SHIFT_JIS; - } - mCharsetString = charset; mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS; } else { - mCharsetString = UTF_8; - mVCardCharsetParameter = "CHARSET=" + UTF_8; + if (TextUtils.isEmpty(charset)) { + Log.i(LOG_TAG, + "Use the charset \"" + VCardConfig.DEFAULT_EXPORT_CHARSET + + "\" for export."); + mCharset = VCardConfig.DEFAULT_EXPORT_CHARSET; + mVCardCharsetParameter = "CHARSET=" + VCardConfig.DEFAULT_EXPORT_CHARSET; + } else { + try { + charset = CharsetUtils.charsetForVendor(charset).name(); + } catch (UnsupportedCharsetException e) { + Log.i(LOG_TAG, + "Career-specific \"" + charset + "\" was not found (as usual). " + + "Use it as is."); + } + mCharset = charset; + mVCardCharsetParameter = "CHARSET=" + charset; + } } clear(); } @@ -379,8 +437,8 @@ public class VCardBuilder { mBuilder.append(VCardConstants.PROPERTY_FN); // Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it - // when it would be useful for external importers, assuming no external - // importer allows this vioration. + // when it would be useful or necessary for external importers, + // assuming the external importer allows this vioration of the spec. if (shouldAppendCharsetParam(displayName)) { mBuilder.append(VCARD_PARAM_SEPARATOR); mBuilder.append(mVCardCharsetParameter); @@ -454,18 +512,18 @@ public class VCardBuilder { mBuilder.append(VCARD_END_OF_LINE); } else if (mIsJapaneseMobilePhone) { // Note: There is no appropriate property for expressing - // phonetic name in vCard 2.1, while there is in + // phonetic name (Yomigana in Japanese) in vCard 2.1, while there is in // vCard 3.0 (SORT-STRING). - // We chose to use DoCoMo's way when the device is Japanese one - // since it is supported by - // a lot of Japanese mobile phones. This is "X-" property, so - // any parser hopefully would not get confused with this. + // We use DoCoMo's way when the device is Japanese one since it is already + // supported by a lot of Japanese mobile phones. + // This is "X-" property, so any parser hopefully would not get + // confused with this. // // Also, DoCoMo's specification requires vCard composer to use just the first // column. // i.e. - // o SOUND;X-IRMC-N:Miyakawa Daisuke;;;; - // x SOUND;X-IRMC-N:Miyakawa;Daisuke;;; + // good: SOUND;X-IRMC-N:Miyakawa Daisuke;;;; + // bad : SOUND;X-IRMC-N:Miyakawa;Daisuke;;; mBuilder.append(VCardConstants.PROPERTY_SOUND); mBuilder.append(VCARD_PARAM_SEPARATOR); mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N); @@ -519,10 +577,10 @@ public class VCardBuilder { mBuilder.append(encodedPhoneticGivenName); } } - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); // family;given + mBuilder.append(VCARD_ITEM_SEPARATOR); // given;middle + mBuilder.append(VCARD_ITEM_SEPARATOR); // middle;prefix + mBuilder.append(VCARD_ITEM_SEPARATOR); // prefix;suffix mBuilder.append(VCARD_END_OF_LINE); } @@ -549,7 +607,7 @@ public class VCardBuilder { mBuilder.append(VCARD_DATA_SEPARATOR); mBuilder.append(encodedPhoneticGivenName); mBuilder.append(VCARD_END_OF_LINE); - } + } // if (!TextUtils.isEmpty(phoneticGivenName)) if (!TextUtils.isEmpty(phoneticMiddleName)) { final boolean reallyUseQuotedPrintable = (mShouldUseQuotedPrintable && @@ -572,7 +630,7 @@ public class VCardBuilder { mBuilder.append(VCARD_DATA_SEPARATOR); mBuilder.append(encodedPhoneticMiddleName); mBuilder.append(VCARD_END_OF_LINE); - } + } // if (!TextUtils.isEmpty(phoneticGivenName)) if (!TextUtils.isEmpty(phoneticFamilyName)) { final boolean reallyUseQuotedPrintable = (mShouldUseQuotedPrintable && @@ -595,7 +653,7 @@ public class VCardBuilder { mBuilder.append(VCARD_DATA_SEPARATOR); mBuilder.append(encodedPhoneticFamilyName); mBuilder.append(VCARD_END_OF_LINE); - } + } // if (!TextUtils.isEmpty(phoneticFamilyName)) } } @@ -903,21 +961,21 @@ public class VCardBuilder { encodedCountry = escapeCharacters(rawCountry); encodedNeighborhood = escapeCharacters(rawNeighborhood); } - final StringBuffer addressBuffer = new StringBuffer(); - addressBuffer.append(encodedPoBox); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(encodedStreet); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(encodedLocality); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(encodedRegion); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(encodedPostalCode); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(encodedCountry); + final StringBuilder addressBuilder = new StringBuilder(); + addressBuilder.append(encodedPoBox); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street + addressBuilder.append(encodedStreet); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality + addressBuilder.append(encodedLocality); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region + addressBuilder.append(encodedRegion); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code + addressBuilder.append(encodedPostalCode); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country + addressBuilder.append(encodedCountry); return new PostalStruct( - reallyUseQuotedPrintable, appendCharset, addressBuffer.toString()); + reallyUseQuotedPrintable, appendCharset, addressBuilder.toString()); } else { // VCardUtils.areAllEmpty(rawAddressArray) == true // Try to use FORMATTED_ADDRESS instead. final String rawFormattedAddress = @@ -940,16 +998,16 @@ public class VCardBuilder { // We use the second value ("Extended Address") just because Japanese mobile phones // do so. If the other importer expects the value be in the other field, some flag may // be needed. - final StringBuffer addressBuffer = new StringBuffer(); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(encodedFormattedAddress); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(VCARD_ITEM_SEPARATOR); - addressBuffer.append(VCARD_ITEM_SEPARATOR); + final StringBuilder addressBuilder = new StringBuilder(); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address + addressBuilder.append(encodedFormattedAddress); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country return new PostalStruct( - reallyUseQuotedPrintable, appendCharset, addressBuffer.toString()); + reallyUseQuotedPrintable, appendCharset, addressBuilder.toString()); } } @@ -1146,6 +1204,8 @@ public class VCardBuilder { } public VCardBuilder appendEvents(final List
- * The class for composing VCard from Contacts information. Note that this is - * completely differnt implementation from - * android.syncml.pim.vcard.VCardComposer, which is not maintained anymore. + * The class for composing vCard from Contacts information. *
- * ** Usually, this class should be used like this. *
- * *VCardComposer composer = null;
* try {
* composer = new VCardComposer(context);
@@ -94,14 +91,17 @@ import java.util.Map;
* composer.terminate();
* }
* }
+ * + * Users have to manually take care of memory efficiency. Even one vCard may contain + * image of non-trivial size for mobile devices. + *
+ *+ * In default, Default {@link VCardBuilder} class is used to build each vCard. + *
*/ public class VCardComposer { private static final String LOG_TAG = "VCardComposer"; - public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME; - public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME; - public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER; - public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO = "Failed to get database information"; @@ -119,6 +119,8 @@ public class VCardComposer { public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; + // Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here, + // since usual vCard devices for Japanese devices already use it. private static final String SHIFT_JIS = "SHIFT_JIS"; private static final String UTF_8 = "UTF-8"; @@ -141,7 +143,7 @@ public class VCardComposer { sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ); sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER); sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME); - // Google talk is a special case. + // We don't add Google talk here since it has to be handled separately. } public static interface OneEntryHandler { @@ -152,37 +154,37 @@ public class VCardComposer { /** *- * An useful example handler, which emits VCard String to outputstream one by one. + * An useful handler for emitting vCard String to an OutputStream object one by one. *
** The input OutputStream object is closed() on {@link #onTerminate()}. - * Must not close the stream outside. + * Must not close the stream outside this class. *
*/ public class HandlerForOutputStream implements OneEntryHandler { @SuppressWarnings("hiding") private static final String LOG_TAG = "vcard.VCardComposer.HandlerForOutputStream"; - final private OutputStream mOutputStream; // mWriter will close this. - private Writer mWriter; - private boolean mOnTerminateIsCalled = false; + final private OutputStream mOutputStream; // mWriter will close this. + protected Writer mWriter; + /** * Input stream will be closed on the detruction of this object. */ - public HandlerForOutputStream(OutputStream outputStream) { + public HandlerForOutputStream(final OutputStream outputStream) { mOutputStream = outputStream; } - public boolean onInit(Context context) { + public final boolean onInit(final Context context) { try { mWriter = new BufferedWriter(new OutputStreamWriter( - mOutputStream, mCharsetString)); + mOutputStream, mCharset)); } catch (UnsupportedEncodingException e1) { - Log.e(LOG_TAG, "Unsupported charset: " + mCharsetString); + Log.e(LOG_TAG, "Unsupported charset: " + mCharset); mErrorReason = "Encoding is not supported (usually this does not happen!): " - + mCharsetString; + + mCharset; return false; } @@ -205,7 +207,7 @@ public class VCardComposer { return true; } - public boolean onEntryCreated(String vcard) { + public final boolean onEntryCreated(String vcard) { try { mWriter.write(vcard); } catch (IOException e) { @@ -218,7 +220,7 @@ public class VCardComposer { return true; } - public void onTerminate() { + public final void onTerminate() { mOnTerminateIsCalled = true; if (mWriter != null) { try { @@ -235,14 +237,21 @@ public class VCardComposer { "IOException during closing the output stream: " + e.getMessage()); } finally { - try { - mWriter.close(); - } catch (IOException e) { - } + closeOutputStream(); } } } + // Users can override this if they want to (e.g. if they don't want to close the stream). + // TODO: Should expose bare OutputStream instead? + public void closeOutputStream() { + try { + mWriter.close(); + } catch (IOException e) { + Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring."); + } + } + @Override public void finalize() { if (!mOnTerminateIsCalled) { @@ -257,11 +266,10 @@ public class VCardComposer { private final ContentResolver mContentResolver; private final boolean mIsDoCoMo; - private final boolean mUsesShiftJis; private Cursor mCursor; private int mIdColumn; - private final String mCharsetString; + private final String mCharset; private boolean mTerminateIsCalled; private final List+ * The charset used during import. + *
+ *+ * We cannot determine which charset should be used to interpret a given vCard file, + * while we have to decode sime encoded data (e.g. BASE64) to binary. + * In order to avoid "misinterpretation" of charset as much as possible, + * "ISO-8859-1" (a.k.a Latin-1) is first used for reading a stream. + * When charset is specified in a property (with "CHARSET=..." parameter), + * the string is decoded to raw bytes and encoded into the specific charset, + * assuming "ISO-8859-1" is able to map "all" 8bit characters to some unicode, + * and it has 1 to 1 mapping in all 8bit characters. + * If the assumption is not correct, this setting will cause some bug. + *
+ */ + /* package */ static final String DEFAULT_TEMPORARY_CHARSET = "ISO-8859-1"; + + // TODO: still intermediate procedures uses this charset. Fix it. + public static final String DEFAULT_IMPORT_CHARSET = "ISO-8859-1"; + public static final String DEFAULT_EXPORT_CHARSET = "UTF-8"; + public static final int FLAG_V21 = 0; public static final int FLAG_V30 = 1; @@ -58,10 +76,13 @@ public class VCardConfig { private static final int NAME_ORDER_MASK = 0xC; // 0x10 is reserved for safety - - private static final int FLAG_CHARSET_UTF8 = 0; - private static final int FLAG_CHARSET_SHIFT_JIS = 0x100; - private static final int FLAG_CHARSET_MASK = 0xF00; + + /* + * These flags are ignored when charset is explicitly given by a caller. + */ + private static final int FLAG_USE_UTF8_FOR_EXPORT = 0; + private static final int FLAG_USE_SHIFT_JIS_FOR_EXPORT = 0x100; + private static final int FLAG_CHARSET_MASK_FOR_EKPORT = 0xF00; /** * The flag indicating the vCard composer will add some "X-" properties used only in Android @@ -196,7 +217,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V21_GENERIC_UTF8 = - (FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 | + (FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_USE_UTF8_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static String VCARD_TYPE_V21_GENERIC_UTF8_STR = "v21_generic"; @@ -210,7 +231,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V30_GENERIC_UTF8 = - (FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 | + (FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_USE_UTF8_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static final String VCARD_TYPE_V30_GENERIC_UTF8_STR = "v30_generic"; @@ -222,7 +243,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V21_EUROPE_UTF8 = - (FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 | + (FLAG_V21 | NAME_ORDER_EUROPE | FLAG_USE_UTF8_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static final String VCARD_TYPE_V21_EUROPE_UTF8_STR = "v21_europe"; @@ -236,7 +257,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V30_EUROPE_UTF8 = - (FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 | + (FLAG_V30 | NAME_ORDER_EUROPE | FLAG_USE_UTF8_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe"; @@ -250,7 +271,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V21_JAPANESE_UTF8 = - (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 | + (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_UTF8_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8"; @@ -265,7 +286,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V21_JAPANESE_SJIS = - (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS | + (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_SHIFT_JIS_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static final String VCARD_TYPE_V21_JAPANESE_SJIS_STR = "v21_japanese_sjis"; @@ -280,7 +301,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V30_JAPANESE_SJIS = - (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS | + (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_USE_SHIFT_JIS_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static final String VCARD_TYPE_V30_JAPANESE_SJIS_STR = "v30_japanese_sjis"; @@ -294,7 +315,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V30_JAPANESE_UTF8 = - (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 | + (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_USE_UTF8_FOR_EXPORT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); /* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8"; @@ -310,7 +331,7 @@ public class VCardConfig { * */ public static final int VCARD_TYPE_V21_JAPANESE_MOBILE = - (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS | + (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_SHIFT_JIS_FOR_EXPORT | FLAG_CONVERT_PHONETIC_NAME_STRINGS | FLAG_REFRAIN_QP_TO_NAME_PROPERTIES); @@ -379,12 +400,17 @@ public class VCardConfig { return !isV30(vcardType); } - public static boolean usesUtf8(final int vcardType) { - return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_UTF8); + /* package */ static boolean shouldUseUtf8ForExport(final int vcardType) { + return ((vcardType & FLAG_CHARSET_MASK_FOR_EKPORT) == FLAG_USE_UTF8_FOR_EXPORT); } - public static boolean usesShiftJis(final int vcardType) { - return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_SHIFT_JIS); + /** + * Shift_JIS (a charset for Japanese text files) needs special handling to select + * carrer specific variants. + * @hide just for test + */ + public static boolean shouldUseShiftJisForExport(final int vcardType) { + return ((vcardType & FLAG_CHARSET_MASK_FOR_EKPORT) == FLAG_USE_SHIFT_JIS_FOR_EXPORT); } public static int getNameOrderType(final int vcardType) { diff --git a/core/java/android/pim/vcard/VCardEntryConstructor.java b/core/java/android/pim/vcard/VCardEntryConstructor.java index 290ca2bfec14d..4efb105e8abfc 100644 --- a/core/java/android/pim/vcard/VCardEntryConstructor.java +++ b/core/java/android/pim/vcard/VCardEntryConstructor.java @@ -80,7 +80,7 @@ public class VCardEntryConstructor implements VCardInterpreter { if (inputCharset != null) { mInputCharset = inputCharset; } else { - mInputCharset = VCardConfig.DEFAULT_CHARSET; + mInputCharset = VCardConfig.DEFAULT_TEMPORARY_CHARSET; } if (charsetForDetodedBytes != null) { mCharsetForDecodedBytes = charsetForDetodedBytes; diff --git a/core/java/android/pim/vcard/VCardParser.java b/core/java/android/pim/vcard/VCardParser.java index 57c52a646cdde..b38ea93fd3ab3 100644 --- a/core/java/android/pim/vcard/VCardParser.java +++ b/core/java/android/pim/vcard/VCardParser.java @@ -34,26 +34,17 @@ public abstract class VCardParser { /** *- * Parses the given stream and send the VCard data into VCardBuilderBase object. + * Parses the given stream and send the vCard data into VCardBuilderBase object. * * Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets * local encoding to it. For example, Japanese phone career uses Shift_JIS, which is - * formally allowed in VCard 2.1, but not recommended in VCard 3.0. In VCard 2.1, - * In some exreme case, some VCard may have different charsets in one VCard (though - * we do not see any device which emits such kind of malicious data) + * formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1, + * In some exreme case, it is allowed for vCard to have different charsets in one vCard. *
*- * In order to avoid "misunderstanding" charset as much as possible, this method - * use "ISO-8859-1" for reading the stream. When charset is specified in some property - * (with "CHARSET=..." parameter), the string is decoded to raw bytes and encoded to - * the charset. This method assumes that "ISO-8859-1" has 1 to 1 mapping in all 8bit - * characters, which is not completely sure. In some cases, this "decoding-encoding" - * scheme may fail. To avoid the case, - *
- *- * We recommend you to use {@link VCardSourceDetector} and detect which kind of source the - * VCard comes from and explicitly specify a charset using the result. + * We recommend you use {@link VCardSourceDetector} and detect which kind of source the + * vCard comes from and explicitly specify a charset using the result. *
* * @param is The source to parse. @@ -61,27 +52,24 @@ public abstract class VCardParser { * @return Returns true for success. Otherwise returns false. * @throws IOException, VCardException */ - public abstract boolean parse(InputStream is, VCardInterpreter interepreter) - throws IOException, VCardException; - + public final boolean parse(InputStream is, VCardInterpreter interepreter) + throws IOException, VCardException { + return parse(is, VCardConfig.DEFAULT_TEMPORARY_CHARSET, interepreter); + } + /** ** The method variants which accept charset. *
- *- * RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use - * UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese - * phone uses Shift_JIS as a charset (e.g. W61SH), and another uses - * "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification (e.g. W53K). - *
* * @param is The source to parse. * @param charset Charset to be used. - * @param builder The VCardBuilderBase object. + * @param interpreter The VCardBuilderBase object. * @return Returns true when successful. Otherwise returns false. * @throws IOException, VCardException */ - public abstract boolean parse(InputStream is, String charset, VCardInterpreter builder) + public abstract boolean parse(InputStream is, String charset, + VCardInterpreter interpreter) throws IOException, VCardException; /** diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java index fe8cfb07776c1..4f8515060501c 100644 --- a/core/java/android/pim/vcard/VCardParser_V21.java +++ b/core/java/android/pim/vcard/VCardParser_V21.java @@ -68,7 +68,28 @@ public class VCardParser_V21 extends VCardParser { private static final HashSet