Loading core/java/android/pim/vcard/ContactStruct.java +52 −32 Original line number Diff line number Diff line Loading @@ -20,8 +20,10 @@ import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; import android.os.RemoteException; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Groups; import android.provider.ContactsContract.RawContacts; Loading Loading @@ -675,16 +677,53 @@ public class ContactStruct { */ @SuppressWarnings("fallthrough") private void handlePhoneticNameFromSound(List<String> elems) { // Family, Given, Middle. (1-3) // This is not from specification but mere assumption. Some Japanese phones use this order. if (!(TextUtils.isEmpty(mPhoneticFamilyName) && TextUtils.isEmpty(mPhoneticMiddleName) && TextUtils.isEmpty(mPhoneticGivenName))) { // This means the other properties like "X-PHONETIC-FIRST-NAME" was already found. // Ignore "SOUND;X-IRMC-N". return; } int size; if (elems == null || (size = elems.size()) < 1) { return; } // Assume that the order is "Family, Given, Middle". // This is not from specification but mere assumption. Some Japanese phones use this order. if (size > 3) { size = 3; } if (elems.get(0).length() > 0) { boolean onlyFirstElemIsNonEmpty = true; for (int i = 1; i < size; i++) { if (elems.get(i).length() > 0) { onlyFirstElemIsNonEmpty = false; break; } } if (onlyFirstElemIsNonEmpty) { final String[] namesArray = elems.get(0).split(" "); final int nameArrayLength = namesArray.length; if (nameArrayLength == 3) { // Assume the string is "Family Middle Given". mPhoneticFamilyName = namesArray[0]; mPhoneticMiddleName = namesArray[1]; mPhoneticGivenName = namesArray[2]; } else if (nameArrayLength == 2) { // Assume the string is "Family Given" based on the Japanese mobile // phones' preference. mPhoneticFamilyName = namesArray[0]; mPhoneticGivenName = namesArray[1]; } else { mPhoneticFullName = elems.get(0); } return; } } switch (size) { // fallthrough case 3: Loading Loading @@ -976,36 +1015,8 @@ public class ContactStruct { if (!TextUtils.isEmpty(mFullName)) { mDisplayName = mFullName; } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) { StringBuilder builder = new StringBuilder(); List<String> nameList; switch (VCardConfig.getNameOrderType(mVCardType)) { case VCardConfig.NAME_ORDER_JAPANESE: if (VCardUtils.containsOnlyPrintableAscii(mFamilyName) && VCardUtils.containsOnlyPrintableAscii(mGivenName)) { nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix); } else { nameList = Arrays.asList(mPrefix, mFamilyName, mMiddleName, mGivenName, mSuffix); } break; case VCardConfig.NAME_ORDER_EUROPE: nameList = Arrays.asList(mPrefix, mMiddleName, mGivenName, mFamilyName, mSuffix); break; default: nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix); break; } boolean first = true; for (String namePart : nameList) { if (!TextUtils.isEmpty(namePart)) { if (first) { first = false; } else { builder.append(' '); } builder.append(namePart); } } mDisplayName = builder.toString(); mDisplayName = VCardUtils.constructNameFromElements(mVCardType, mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix); } else if (!(TextUtils.isEmpty(mPhoneticFamilyName) && TextUtils.isEmpty(mPhoneticGivenName))) { mDisplayName = VCardUtils.constructNameFromElements(mVCardType, Loading Loading @@ -1282,6 +1293,15 @@ public class ContactStruct { } } public static ContactStruct buildFromResolver(ContentResolver resolver) { return buildFromResolver(resolver, Contacts.CONTENT_URI); } public static ContactStruct buildFromResolver(ContentResolver resolver, Uri uri) { return null; } private boolean nameFieldsAreEmpty() { return (TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mMiddleName) Loading core/java/android/pim/vcard/VCardComposer.java +55 −27 Original line number Diff line number Diff line Loading @@ -682,10 +682,18 @@ public class VCardComposer { final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME); final String prefix = contentValues.getAsString(StructuredName.PREFIX); final String suffix = contentValues.getAsString(StructuredName.SUFFIX); final String phoneticFamilyName = contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME); final String phoneticMiddleName = contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME); final String phoneticGivenName = contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME); final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME); return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) && TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) && TextUtils.isEmpty(suffix) && TextUtils.isEmpty(displayName)); TextUtils.isEmpty(suffix) && TextUtils.isEmpty(phoneticFamilyName) && TextUtils.isEmpty(phoneticMiddleName) && TextUtils.isEmpty(phoneticGivenName) && TextUtils.isEmpty(displayName)); } private void appendStructuredNamesInternal(final StringBuilder builder, Loading Loading @@ -865,9 +873,11 @@ public class VCardComposer { builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_END_OF_LINE); if (mIsV30) { builder.append(Constants.PROPERTY_FN); // TODO: Not allowed formally... // 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. if (shouldAppendCharsetParameter(displayName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); Loading @@ -875,7 +885,6 @@ public class VCardComposer { builder.append(VCARD_DATA_SEPARATOR); builder.append(encodedDisplayName); builder.append(VCARD_END_OF_LINE); } } else if (mIsV30) { // vCard 3.0 specification requires these fields. appendVCardLine(builder, Constants.PROPERTY_N, ""); Loading Loading @@ -913,15 +922,12 @@ public class VCardComposer { .constructNameFromElements(mVCardType, phoneticFamilyName, phoneticMiddleName, phoneticGivenName); builder.append(Constants.PROPERTY_SORT_STRING); // Do not need to care about QP, since vCard 3.0 does not allow it. final String encodedSortString = escapeCharacters(sortString); if (shouldAppendCharsetParameter(encodedSortString)) { if (shouldAppendCharsetParameter(sortString)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } builder.append(VCARD_DATA_SEPARATOR); builder.append(encodedSortString); builder.append(escapeCharacters(sortString)); builder.append(VCARD_END_OF_LINE); } else if (mIsJapaneseMobilePhone) { // Note: There is no appropriate property for expressing Loading Loading @@ -964,11 +970,31 @@ public class VCardComposer { builder.append(mVCardCharsetParameter); } builder.append(VCARD_DATA_SEPARATOR); // DoCoMo's specification requires vCard composer to use just the first // column. { boolean first = true; if (!TextUtils.isEmpty(encodedPhoneticFamilyName)) { builder.append(encodedPhoneticFamilyName); builder.append(VCARD_ITEM_SEPARATOR); first = false; } if (!TextUtils.isEmpty(encodedPhoneticMiddleName)) { if (first) { first = false; } else { builder.append(' '); } builder.append(encodedPhoneticMiddleName); } if (!TextUtils.isEmpty(encodedPhoneticGivenName)) { if (!first) { builder.append(' '); } builder.append(encodedPhoneticGivenName); } } builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_ITEM_SEPARATOR); builder.append(encodedPhoneticMiddleName); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_END_OF_LINE); Loading @@ -988,7 +1014,8 @@ public class VCardComposer { if (mUsesDefactProperty) { if (!TextUtils.isEmpty(phoneticGivenName)) { final boolean reallyUseQuotedPrintable = !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName); (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName)); final String encodedPhoneticGivenName; if (reallyUseQuotedPrintable) { encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName); Loading @@ -996,7 +1023,7 @@ public class VCardComposer { encodedPhoneticGivenName = escapeCharacters(phoneticGivenName); } builder.append(Constants.PROPERTY_X_PHONETIC_FIRST_NAME); if (shouldAppendCharsetParameter(encodedPhoneticGivenName)) { if (shouldAppendCharsetParameter(phoneticGivenName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } Loading @@ -1010,7 +1037,8 @@ public class VCardComposer { } if (!TextUtils.isEmpty(phoneticMiddleName)) { final boolean reallyUseQuotedPrintable = !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName); (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName)); final String encodedPhoneticMiddleName; if (reallyUseQuotedPrintable) { encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName); Loading @@ -1018,7 +1046,7 @@ public class VCardComposer { encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName); } builder.append(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME); if (shouldAppendCharsetParameter(encodedPhoneticMiddleName)) { if (shouldAppendCharsetParameter(phoneticMiddleName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } Loading @@ -1032,7 +1060,8 @@ public class VCardComposer { } if (!TextUtils.isEmpty(phoneticFamilyName)) { final boolean reallyUseQuotedPrintable = !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName); (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName)); final String encodedPhoneticFamilyName; if (reallyUseQuotedPrintable) { encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName); Loading @@ -1040,7 +1069,7 @@ public class VCardComposer { encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName); } builder.append(Constants.PROPERTY_X_PHONETIC_LAST_NAME); if (shouldAppendCharsetParameter(encodedPhoneticFamilyName)) { if (shouldAppendCharsetParameter(phoneticFamilyName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } Loading Loading @@ -2255,8 +2284,7 @@ public class VCardComposer { * to know this text is NOT UTF-8 but Shift_Jis. */ private boolean shouldAppendCharsetParameter(final String propertyValue) { return (!VCardUtils.containsOnlyPrintableAscii(propertyValue) && (!mIsV30 || !mUsesUtf8)); return (!(mIsV30 && mUsesUtf8) && !VCardUtils.containsOnlyPrintableAscii(propertyValue)); } private boolean shouldAppendCharsetParameters(final List<String> propertyValueList) { Loading core/java/android/pim/vcard/VCardConfig.java +20 −19 Original line number Diff line number Diff line Loading @@ -60,7 +60,8 @@ public class VCardConfig { // 0x10 is reserved for safety private static final int FLAG_CHARSET_UTF8 = 0; private static final int FLAG_CHARSET_SHIFT_JIS = 0x20; private static final int FLAG_CHARSET_SHIFT_JIS = 0x100; private static final int FLAG_CHARSET_MASK = 0xF00; /** * The flag indicating the vCard composer will add some "X-" properties used only in Android Loading Loading @@ -349,8 +350,8 @@ public class VCardConfig { sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO); } public static int getVCardTypeFromString(String vcardTypeString) { String loweredKey = vcardTypeString.toLowerCase(); public static int getVCardTypeFromString(final String vcardTypeString) { final String loweredKey = vcardTypeString.toLowerCase(); if (sVCardTypeMap.containsKey(loweredKey)) { return sVCardTypeMap.get(loweredKey); } else { Loading @@ -360,31 +361,31 @@ public class VCardConfig { } } public static boolean isV30(int vcardType) { public static boolean isV30(final int vcardType) { return ((vcardType & FLAG_V30) != 0); } public static boolean usesQuotedPrintable(int vcardType) { public static boolean usesQuotedPrintable(final int vcardType) { return !isV30(vcardType); } public static boolean usesUtf8(int vcardType) { return ((vcardType & FLAG_CHARSET_UTF8) != 0); public static boolean usesUtf8(final int vcardType) { return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_UTF8); } public static boolean usesShiftJis(int vcardType) { return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0); public static boolean usesShiftJis(final int vcardType) { return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_SHIFT_JIS); } public static int getNameOrderType(int vcardType) { public static int getNameOrderType(final int vcardType) { return vcardType & NAME_ORDER_MASK; } public static boolean usesAndroidSpecificProperty(int vcardType) { public static boolean usesAndroidSpecificProperty(final int vcardType) { return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0); } public static boolean usesDefactProperty(int vcardType) { public static boolean usesDefactProperty(final int vcardType) { return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0); } Loading @@ -392,12 +393,12 @@ public class VCardConfig { return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0; } public static boolean refrainsQPToPrimaryProperties(int vcardType) { public static boolean refrainsQPToPrimaryProperties(final int vcardType) { return (!usesQuotedPrintable(vcardType) || ((vcardType & FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES) != 0)); } public static boolean appendTypeParamName(int vcardType) { public static boolean appendTypeParamName(final int vcardType) { return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0)); } Loading @@ -405,19 +406,19 @@ public class VCardConfig { * @return true if the device is Japanese and some Japanese convension is * applied to creating "formatted" something like FORMATTED_ADDRESS. */ public static boolean isJapaneseDevice(int vcardType) { public static boolean isJapaneseDevice(final int vcardType) { return sJapaneseMobileTypeSet.contains(vcardType); } public static boolean needsToConvertPhoneticString(int vcardType) { public static boolean needsToConvertPhoneticString(final int vcardType) { return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0); } public static boolean onlyOneNoteFieldIsAvailable(int vcardType) { public static boolean onlyOneNoteFieldIsAvailable(final int vcardType) { return vcardType == VCARD_TYPE_DOCOMO; } public static boolean isDoCoMo(int vcardType) { public static boolean isDoCoMo(final int vcardType) { return ((vcardType & FLAG_DOCOMO) != 0); } Loading core/java/android/pim/vcard/VCardUtils.java +40 −30 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; import java.util.Collection; Loading Loading @@ -181,27 +182,37 @@ public class VCardUtils { sPhoneTypesSetUnknownToContacts.contains(phoneType)); } public static String[] sortNameElements(int vcardType, String familyName, String middleName, String givenName) { String[] list = new String[3]; switch (VCardConfig.getNameOrderType(vcardType)) { case VCardConfig.NAME_ORDER_JAPANESE: // TODO: Should handle Ascii case? public static String[] sortNameElements(final int vcardType, final String familyName, final String middleName, final String givenName) { final String[] list = new String[3]; final int nameOrderType = VCardConfig.getNameOrderType(vcardType); switch (nameOrderType) { case VCardConfig.NAME_ORDER_JAPANESE: { if (containsOnlyPrintableAscii(familyName) && containsOnlyPrintableAscii(givenName)) { list[0] = givenName; list[1] = middleName; list[2] = familyName; } else { list[0] = familyName; list[1] = middleName; list[2] = givenName; } break; case VCardConfig.NAME_ORDER_EUROPE: } case VCardConfig.NAME_ORDER_EUROPE: { list[0] = middleName; list[1] = givenName; list[2] = familyName; break; default: } default: { list[0] = givenName; list[1] = middleName; list[2] = familyName; break; } } return list; } Loading Loading @@ -302,24 +313,23 @@ public class VCardUtils { return dataArray; } public static String constructNameFromElements(int nameOrderType, String familyName, String middleName, String givenName) { return constructNameFromElements(nameOrderType, familyName, middleName, givenName, public static String constructNameFromElements(final int vcardType, final String familyName, final String middleName, final String givenName) { return constructNameFromElements(vcardType, familyName, middleName, givenName, null, null); } public static String constructNameFromElements(int nameOrderType, String familyName, String middleName, String givenName, String prefix, String suffix) { StringBuilder builder = new StringBuilder(); String[] nameList = sortNameElements(nameOrderType, familyName, middleName, givenName); public static String constructNameFromElements(final int vcardType, final String familyName, final String middleName, final String givenName, final String prefix, final String suffix) { final StringBuilder builder = new StringBuilder(); final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName); boolean first = true; if (!TextUtils.isEmpty(prefix)) { first = false; builder.append(prefix); } for (String namePart : nameList) { for (final String namePart : nameList) { if (!TextUtils.isEmpty(namePart)) { if (first) { first = false; Loading tests/AndroidTests/res/raw/v21_invalid_comment_line.vcf 0 → 100644 +10 −0 Original line number Diff line number Diff line BEGIN:vCard VERSION:2.1 UID:357 N:;Conference Call FN:Conference Call # This line must be ignored. NOTE;ENCODING=QUOTED-PRINTABLE:This is an (sharp ->= #<- sharp) example. This message must NOT be ignored. # This line must be ignored too. END:vCard Loading
core/java/android/pim/vcard/ContactStruct.java +52 −32 Original line number Diff line number Diff line Loading @@ -20,8 +20,10 @@ import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; import android.os.RemoteException; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Groups; import android.provider.ContactsContract.RawContacts; Loading Loading @@ -675,16 +677,53 @@ public class ContactStruct { */ @SuppressWarnings("fallthrough") private void handlePhoneticNameFromSound(List<String> elems) { // Family, Given, Middle. (1-3) // This is not from specification but mere assumption. Some Japanese phones use this order. if (!(TextUtils.isEmpty(mPhoneticFamilyName) && TextUtils.isEmpty(mPhoneticMiddleName) && TextUtils.isEmpty(mPhoneticGivenName))) { // This means the other properties like "X-PHONETIC-FIRST-NAME" was already found. // Ignore "SOUND;X-IRMC-N". return; } int size; if (elems == null || (size = elems.size()) < 1) { return; } // Assume that the order is "Family, Given, Middle". // This is not from specification but mere assumption. Some Japanese phones use this order. if (size > 3) { size = 3; } if (elems.get(0).length() > 0) { boolean onlyFirstElemIsNonEmpty = true; for (int i = 1; i < size; i++) { if (elems.get(i).length() > 0) { onlyFirstElemIsNonEmpty = false; break; } } if (onlyFirstElemIsNonEmpty) { final String[] namesArray = elems.get(0).split(" "); final int nameArrayLength = namesArray.length; if (nameArrayLength == 3) { // Assume the string is "Family Middle Given". mPhoneticFamilyName = namesArray[0]; mPhoneticMiddleName = namesArray[1]; mPhoneticGivenName = namesArray[2]; } else if (nameArrayLength == 2) { // Assume the string is "Family Given" based on the Japanese mobile // phones' preference. mPhoneticFamilyName = namesArray[0]; mPhoneticGivenName = namesArray[1]; } else { mPhoneticFullName = elems.get(0); } return; } } switch (size) { // fallthrough case 3: Loading Loading @@ -976,36 +1015,8 @@ public class ContactStruct { if (!TextUtils.isEmpty(mFullName)) { mDisplayName = mFullName; } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) { StringBuilder builder = new StringBuilder(); List<String> nameList; switch (VCardConfig.getNameOrderType(mVCardType)) { case VCardConfig.NAME_ORDER_JAPANESE: if (VCardUtils.containsOnlyPrintableAscii(mFamilyName) && VCardUtils.containsOnlyPrintableAscii(mGivenName)) { nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix); } else { nameList = Arrays.asList(mPrefix, mFamilyName, mMiddleName, mGivenName, mSuffix); } break; case VCardConfig.NAME_ORDER_EUROPE: nameList = Arrays.asList(mPrefix, mMiddleName, mGivenName, mFamilyName, mSuffix); break; default: nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix); break; } boolean first = true; for (String namePart : nameList) { if (!TextUtils.isEmpty(namePart)) { if (first) { first = false; } else { builder.append(' '); } builder.append(namePart); } } mDisplayName = builder.toString(); mDisplayName = VCardUtils.constructNameFromElements(mVCardType, mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix); } else if (!(TextUtils.isEmpty(mPhoneticFamilyName) && TextUtils.isEmpty(mPhoneticGivenName))) { mDisplayName = VCardUtils.constructNameFromElements(mVCardType, Loading Loading @@ -1282,6 +1293,15 @@ public class ContactStruct { } } public static ContactStruct buildFromResolver(ContentResolver resolver) { return buildFromResolver(resolver, Contacts.CONTENT_URI); } public static ContactStruct buildFromResolver(ContentResolver resolver, Uri uri) { return null; } private boolean nameFieldsAreEmpty() { return (TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mMiddleName) Loading
core/java/android/pim/vcard/VCardComposer.java +55 −27 Original line number Diff line number Diff line Loading @@ -682,10 +682,18 @@ public class VCardComposer { final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME); final String prefix = contentValues.getAsString(StructuredName.PREFIX); final String suffix = contentValues.getAsString(StructuredName.SUFFIX); final String phoneticFamilyName = contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME); final String phoneticMiddleName = contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME); final String phoneticGivenName = contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME); final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME); return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) && TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) && TextUtils.isEmpty(suffix) && TextUtils.isEmpty(displayName)); TextUtils.isEmpty(suffix) && TextUtils.isEmpty(phoneticFamilyName) && TextUtils.isEmpty(phoneticMiddleName) && TextUtils.isEmpty(phoneticGivenName) && TextUtils.isEmpty(displayName)); } private void appendStructuredNamesInternal(final StringBuilder builder, Loading Loading @@ -865,9 +873,11 @@ public class VCardComposer { builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_END_OF_LINE); if (mIsV30) { builder.append(Constants.PROPERTY_FN); // TODO: Not allowed formally... // 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. if (shouldAppendCharsetParameter(displayName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); Loading @@ -875,7 +885,6 @@ public class VCardComposer { builder.append(VCARD_DATA_SEPARATOR); builder.append(encodedDisplayName); builder.append(VCARD_END_OF_LINE); } } else if (mIsV30) { // vCard 3.0 specification requires these fields. appendVCardLine(builder, Constants.PROPERTY_N, ""); Loading Loading @@ -913,15 +922,12 @@ public class VCardComposer { .constructNameFromElements(mVCardType, phoneticFamilyName, phoneticMiddleName, phoneticGivenName); builder.append(Constants.PROPERTY_SORT_STRING); // Do not need to care about QP, since vCard 3.0 does not allow it. final String encodedSortString = escapeCharacters(sortString); if (shouldAppendCharsetParameter(encodedSortString)) { if (shouldAppendCharsetParameter(sortString)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } builder.append(VCARD_DATA_SEPARATOR); builder.append(encodedSortString); builder.append(escapeCharacters(sortString)); builder.append(VCARD_END_OF_LINE); } else if (mIsJapaneseMobilePhone) { // Note: There is no appropriate property for expressing Loading Loading @@ -964,11 +970,31 @@ public class VCardComposer { builder.append(mVCardCharsetParameter); } builder.append(VCARD_DATA_SEPARATOR); // DoCoMo's specification requires vCard composer to use just the first // column. { boolean first = true; if (!TextUtils.isEmpty(encodedPhoneticFamilyName)) { builder.append(encodedPhoneticFamilyName); builder.append(VCARD_ITEM_SEPARATOR); first = false; } if (!TextUtils.isEmpty(encodedPhoneticMiddleName)) { if (first) { first = false; } else { builder.append(' '); } builder.append(encodedPhoneticMiddleName); } if (!TextUtils.isEmpty(encodedPhoneticGivenName)) { if (!first) { builder.append(' '); } builder.append(encodedPhoneticGivenName); } } builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_ITEM_SEPARATOR); builder.append(encodedPhoneticMiddleName); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_ITEM_SEPARATOR); builder.append(VCARD_END_OF_LINE); Loading @@ -988,7 +1014,8 @@ public class VCardComposer { if (mUsesDefactProperty) { if (!TextUtils.isEmpty(phoneticGivenName)) { final boolean reallyUseQuotedPrintable = !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName); (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName)); final String encodedPhoneticGivenName; if (reallyUseQuotedPrintable) { encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName); Loading @@ -996,7 +1023,7 @@ public class VCardComposer { encodedPhoneticGivenName = escapeCharacters(phoneticGivenName); } builder.append(Constants.PROPERTY_X_PHONETIC_FIRST_NAME); if (shouldAppendCharsetParameter(encodedPhoneticGivenName)) { if (shouldAppendCharsetParameter(phoneticGivenName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } Loading @@ -1010,7 +1037,8 @@ public class VCardComposer { } if (!TextUtils.isEmpty(phoneticMiddleName)) { final boolean reallyUseQuotedPrintable = !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName); (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName)); final String encodedPhoneticMiddleName; if (reallyUseQuotedPrintable) { encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName); Loading @@ -1018,7 +1046,7 @@ public class VCardComposer { encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName); } builder.append(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME); if (shouldAppendCharsetParameter(encodedPhoneticMiddleName)) { if (shouldAppendCharsetParameter(phoneticMiddleName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } Loading @@ -1032,7 +1060,8 @@ public class VCardComposer { } if (!TextUtils.isEmpty(phoneticFamilyName)) { final boolean reallyUseQuotedPrintable = !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName); (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName)); final String encodedPhoneticFamilyName; if (reallyUseQuotedPrintable) { encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName); Loading @@ -1040,7 +1069,7 @@ public class VCardComposer { encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName); } builder.append(Constants.PROPERTY_X_PHONETIC_LAST_NAME); if (shouldAppendCharsetParameter(encodedPhoneticFamilyName)) { if (shouldAppendCharsetParameter(phoneticFamilyName)) { builder.append(VCARD_PARAM_SEPARATOR); builder.append(mVCardCharsetParameter); } Loading Loading @@ -2255,8 +2284,7 @@ public class VCardComposer { * to know this text is NOT UTF-8 but Shift_Jis. */ private boolean shouldAppendCharsetParameter(final String propertyValue) { return (!VCardUtils.containsOnlyPrintableAscii(propertyValue) && (!mIsV30 || !mUsesUtf8)); return (!(mIsV30 && mUsesUtf8) && !VCardUtils.containsOnlyPrintableAscii(propertyValue)); } private boolean shouldAppendCharsetParameters(final List<String> propertyValueList) { Loading
core/java/android/pim/vcard/VCardConfig.java +20 −19 Original line number Diff line number Diff line Loading @@ -60,7 +60,8 @@ public class VCardConfig { // 0x10 is reserved for safety private static final int FLAG_CHARSET_UTF8 = 0; private static final int FLAG_CHARSET_SHIFT_JIS = 0x20; private static final int FLAG_CHARSET_SHIFT_JIS = 0x100; private static final int FLAG_CHARSET_MASK = 0xF00; /** * The flag indicating the vCard composer will add some "X-" properties used only in Android Loading Loading @@ -349,8 +350,8 @@ public class VCardConfig { sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO); } public static int getVCardTypeFromString(String vcardTypeString) { String loweredKey = vcardTypeString.toLowerCase(); public static int getVCardTypeFromString(final String vcardTypeString) { final String loweredKey = vcardTypeString.toLowerCase(); if (sVCardTypeMap.containsKey(loweredKey)) { return sVCardTypeMap.get(loweredKey); } else { Loading @@ -360,31 +361,31 @@ public class VCardConfig { } } public static boolean isV30(int vcardType) { public static boolean isV30(final int vcardType) { return ((vcardType & FLAG_V30) != 0); } public static boolean usesQuotedPrintable(int vcardType) { public static boolean usesQuotedPrintable(final int vcardType) { return !isV30(vcardType); } public static boolean usesUtf8(int vcardType) { return ((vcardType & FLAG_CHARSET_UTF8) != 0); public static boolean usesUtf8(final int vcardType) { return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_UTF8); } public static boolean usesShiftJis(int vcardType) { return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0); public static boolean usesShiftJis(final int vcardType) { return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_SHIFT_JIS); } public static int getNameOrderType(int vcardType) { public static int getNameOrderType(final int vcardType) { return vcardType & NAME_ORDER_MASK; } public static boolean usesAndroidSpecificProperty(int vcardType) { public static boolean usesAndroidSpecificProperty(final int vcardType) { return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0); } public static boolean usesDefactProperty(int vcardType) { public static boolean usesDefactProperty(final int vcardType) { return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0); } Loading @@ -392,12 +393,12 @@ public class VCardConfig { return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0; } public static boolean refrainsQPToPrimaryProperties(int vcardType) { public static boolean refrainsQPToPrimaryProperties(final int vcardType) { return (!usesQuotedPrintable(vcardType) || ((vcardType & FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES) != 0)); } public static boolean appendTypeParamName(int vcardType) { public static boolean appendTypeParamName(final int vcardType) { return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0)); } Loading @@ -405,19 +406,19 @@ public class VCardConfig { * @return true if the device is Japanese and some Japanese convension is * applied to creating "formatted" something like FORMATTED_ADDRESS. */ public static boolean isJapaneseDevice(int vcardType) { public static boolean isJapaneseDevice(final int vcardType) { return sJapaneseMobileTypeSet.contains(vcardType); } public static boolean needsToConvertPhoneticString(int vcardType) { public static boolean needsToConvertPhoneticString(final int vcardType) { return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0); } public static boolean onlyOneNoteFieldIsAvailable(int vcardType) { public static boolean onlyOneNoteFieldIsAvailable(final int vcardType) { return vcardType == VCARD_TYPE_DOCOMO; } public static boolean isDoCoMo(int vcardType) { public static boolean isDoCoMo(final int vcardType) { return ((vcardType & FLAG_DOCOMO) != 0); } Loading
core/java/android/pim/vcard/VCardUtils.java +40 −30 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; import java.util.Collection; Loading Loading @@ -181,27 +182,37 @@ public class VCardUtils { sPhoneTypesSetUnknownToContacts.contains(phoneType)); } public static String[] sortNameElements(int vcardType, String familyName, String middleName, String givenName) { String[] list = new String[3]; switch (VCardConfig.getNameOrderType(vcardType)) { case VCardConfig.NAME_ORDER_JAPANESE: // TODO: Should handle Ascii case? public static String[] sortNameElements(final int vcardType, final String familyName, final String middleName, final String givenName) { final String[] list = new String[3]; final int nameOrderType = VCardConfig.getNameOrderType(vcardType); switch (nameOrderType) { case VCardConfig.NAME_ORDER_JAPANESE: { if (containsOnlyPrintableAscii(familyName) && containsOnlyPrintableAscii(givenName)) { list[0] = givenName; list[1] = middleName; list[2] = familyName; } else { list[0] = familyName; list[1] = middleName; list[2] = givenName; } break; case VCardConfig.NAME_ORDER_EUROPE: } case VCardConfig.NAME_ORDER_EUROPE: { list[0] = middleName; list[1] = givenName; list[2] = familyName; break; default: } default: { list[0] = givenName; list[1] = middleName; list[2] = familyName; break; } } return list; } Loading Loading @@ -302,24 +313,23 @@ public class VCardUtils { return dataArray; } public static String constructNameFromElements(int nameOrderType, String familyName, String middleName, String givenName) { return constructNameFromElements(nameOrderType, familyName, middleName, givenName, public static String constructNameFromElements(final int vcardType, final String familyName, final String middleName, final String givenName) { return constructNameFromElements(vcardType, familyName, middleName, givenName, null, null); } public static String constructNameFromElements(int nameOrderType, String familyName, String middleName, String givenName, String prefix, String suffix) { StringBuilder builder = new StringBuilder(); String[] nameList = sortNameElements(nameOrderType, familyName, middleName, givenName); public static String constructNameFromElements(final int vcardType, final String familyName, final String middleName, final String givenName, final String prefix, final String suffix) { final StringBuilder builder = new StringBuilder(); final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName); boolean first = true; if (!TextUtils.isEmpty(prefix)) { first = false; builder.append(prefix); } for (String namePart : nameList) { for (final String namePart : nameList) { if (!TextUtils.isEmpty(namePart)) { if (first) { first = false; Loading
tests/AndroidTests/res/raw/v21_invalid_comment_line.vcf 0 → 100644 +10 −0 Original line number Diff line number Diff line BEGIN:vCard VERSION:2.1 UID:357 N:;Conference Call FN:Conference Call # This line must be ignored. NOTE;ENCODING=QUOTED-PRINTABLE:This is an (sharp ->= #<- sharp) example. This message must NOT be ignored. # This line must be ignored too. END:vCard