Merge change I82be558f into eclair

* changes:
  Fix vCard composer in three points.
This commit is contained in:
Android (Google) Code Review
2009-09-30 14:11:52 -04:00
2 changed files with 249 additions and 75 deletions

View File

@ -284,7 +284,9 @@ public class VCardComposer {
private final boolean mUsesQuotedPrintable;
private final boolean mUsesAndroidProperty;
private final boolean mUsesDefactProperty;
private final boolean mUsesUtf8;
private final boolean mUsesShiftJis;
private final boolean mUsesQPToPrimaryProperties;
private Cursor mCursor;
private int mIdColumn;
@ -366,7 +368,9 @@ public class VCardComposer {
mUsesAndroidProperty = VCardConfig
.usesAndroidSpecificProperty(vcardType);
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
mUsesQPToPrimaryProperties = VCardConfig.usesQPToPrimaryProperties(vcardType);
if (mIsDoCoMo) {
mCharsetString = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
@ -400,6 +404,7 @@ public class VCardComposer {
if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) {
needCharset = true;
}
// TODO: QP should be used? Using mUsesQPToPrimaryProperties should help.
appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, phoneName, needCharset, false);
appendVCardLine(builder, VCARD_PROPERTY_NAME, phoneName, needCharset, false);
@ -598,6 +603,7 @@ public class VCardComposer {
name = mCursor.getString(NUMBER_COLUMN_INDEX);
}
final boolean needCharset = !(VCardUtils.containsOnlyPrintableAscii(name));
// TODO: QP should be used? Using mUsesQPToPrimaryProperties should help.
appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, name, needCharset, false);
appendVCardLine(builder, VCARD_PROPERTY_NAME, name, needCharset, false);
@ -634,8 +640,8 @@ public class VCardComposer {
ContentValues contentValues = namedContentValues.values;
String key = contentValues.getAsString(Data.MIMETYPE);
if (key != null) {
List<ContentValues> contentValuesList = contentValuesListMap
.get(key);
List<ContentValues> contentValuesList =
contentValuesListMap.get(key);
if (contentValuesList == null) {
contentValuesList = new ArrayList<ContentValues>();
contentValuesListMap.put(key, contentValuesList);
@ -788,26 +794,46 @@ public class VCardComposer {
final String displayName = primaryContentValues
.getAsString(StructuredName.DISPLAY_NAME);
// For now, some primary element is not encoded into Quoted-Printable, which is not
// valid in vCard spec strictly. In the future, we may have to have some flag to
// enable composer to encode these primary field into Quoted-Printable.
if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) {
final String encodedFamily = escapeCharacters(familyName);
final String encodedGiven = escapeCharacters(givenName);
final String encodedMiddle = escapeCharacters(middleName);
final String encodedPrefix = escapeCharacters(prefix);
final String encodedSuffix = escapeCharacters(suffix);
final String encodedFamily;
final String encodedGiven;
final String encodedMiddle;
final String encodedPrefix;
final String encodedSuffix;
final boolean reallyUseQuotedPrintableToName =
(mUsesQPToPrimaryProperties &&
!(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(prefix) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(suffix)));
if (reallyUseQuotedPrintableToName) {
encodedFamily = encodeQuotedPrintable(familyName);
encodedGiven = encodeQuotedPrintable(givenName);
encodedMiddle = encodeQuotedPrintable(middleName);
encodedPrefix = encodeQuotedPrintable(prefix);
encodedSuffix = encodeQuotedPrintable(suffix);
} else {
encodedFamily = escapeCharacters(familyName);
encodedGiven = escapeCharacters(givenName);
encodedMiddle = escapeCharacters(middleName);
encodedPrefix = escapeCharacters(prefix);
encodedSuffix = escapeCharacters(suffix);
}
// N property. This order is specified by vCard spec and does not depend on countries.
builder.append(VCARD_PROPERTY_NAME);
if (!(VCardUtils.containsOnlyPrintableAscii(familyName) &&
VCardUtils.containsOnlyPrintableAscii(givenName) &&
VCardUtils.containsOnlyPrintableAscii(middleName) &&
VCardUtils.containsOnlyPrintableAscii(prefix) &&
VCardUtils.containsOnlyPrintableAscii(suffix))) {
if (shouldAppendCharsetAttribute(Arrays.asList(
familyName, givenName, middleName, prefix, suffix))) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
if (reallyUseQuotedPrintableToName) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(VCARD_ATTR_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(encodedFamily);
@ -821,25 +847,51 @@ public class VCardComposer {
builder.append(encodedSuffix);
builder.append(VCARD_COL_SEPARATOR);
final String encodedFullname = VCardUtils.constructNameFromElements(
final String fullname = VCardUtils.constructNameFromElements(
VCardConfig.getNameOrderType(mVCardType),
encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix);
final boolean reallyUseQuotedPrintableToFullname =
mUsesQPToPrimaryProperties &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(fullname);
final String encodedFullname =
reallyUseQuotedPrintableToFullname ?
encodeQuotedPrintable(fullname) :
escapeCharacters(fullname);
// FN property
builder.append(VCARD_PROPERTY_FULL_NAME);
if (!VCardUtils.containsOnlyPrintableAscii(encodedFullname)) {
if (shouldAppendCharsetAttribute(encodedFullname)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
if (reallyUseQuotedPrintableToFullname) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(VCARD_ATTR_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(encodedFullname);
builder.append(VCARD_COL_SEPARATOR);
} else if (!TextUtils.isEmpty(displayName)) {
final boolean reallyUseQuotedPrintableToDisplayName =
(mUsesQPToPrimaryProperties &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(displayName));
final String encodedDisplayName =
reallyUseQuotedPrintableToDisplayName ?
encodeQuotedPrintable(displayName) :
escapeCharacters(displayName);
builder.append(VCARD_PROPERTY_NAME);
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
if (shouldAppendCharsetAttribute(encodedDisplayName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
if (reallyUseQuotedPrintableToDisplayName) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(VCARD_ATTR_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(escapeCharacters(displayName));
builder.append(encodedDisplayName);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_ITEM_SEPARATOR);
@ -873,54 +925,65 @@ public class VCardComposer {
if (mIsV30) {
final String sortString = VCardUtils
.constructNameFromElements(mVCardType,
phoneticFamilyName, phoneticMiddleName,
phoneticFamilyName,
phoneticMiddleName,
phoneticGivenName);
builder.append(VCARD_PROPERTY_SORT_STRING);
if (!VCardUtils.containsOnlyPrintableAscii(sortString)) {
// Strictly, adding charset information is NOT valid in
// VCard 3.0,
// but we'll add this info since parser side may be able to
// use the charset via
// this attribute field.
//
// e.g. Japanese mobile phones use Shift_Jis while RFC 2426
// recommends
// UTF-8. By adding this field, parsers may be able to know
// this text
// is NOT UTF-8 but Shift_Jis.
// Do not need to care about QP, since vCard 3.0 does not allow it.
final String encodedSortString = escapeCharacters(sortString);
if (shouldAppendCharsetAttribute(encodedSortString)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(sortString);
builder.append(encodedSortString);
builder.append(VCARD_COL_SEPARATOR);
} else {
// Note: There is no appropriate property for expressing
// phonetic name in
// VCard 2.1, while there is in VCard 3.0 (SORT-STRING).
// We chose to use DoCoMo's way since it is supported by a
// lot of Japanese mobile phones.
//
// TODO: should use Quoted-Pritable?
// phonetic name in vCard 2.1, while there is in
// vCard 3.0 (SORT-STRING).
// We chose to use DoCoMo's way 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.
builder.append(VCARD_PROPERTY_SOUND);
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(Constants.ATTR_TYPE_X_IRMC_N);
builder.append(VCARD_ATTR_SEPARATOR);
if (!(VCardUtils.containsOnlyPrintableAscii(phoneticFamilyName) &&
VCardUtils.containsOnlyPrintableAscii(phoneticMiddleName) &&
VCardUtils.containsOnlyPrintableAscii(phoneticGivenName))) {
builder.append(mVCardAttributeCharset);
builder.append(VCARD_DATA_SEPARATOR);
boolean reallyUseQuotedPrintable =
(mUsesQPToPrimaryProperties &&
!(VCardUtils.containsOnlyNonCrLfPrintableAscii(
phoneticFamilyName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(
phoneticMiddleName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(
phoneticGivenName)));
final String encodedPhoneticFamilyName;
final String encodedPhoneticMiddleName;
final String encodedPhoneticGivenName;
if (reallyUseQuotedPrintable) {
encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName);
encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName);
encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName);
} else {
encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName);
encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName);
encodedPhoneticGivenName = escapeCharacters(phoneticGivenName);
}
builder.append(escapeCharacters(phoneticFamilyName));
if (shouldAppendCharsetAttribute(Arrays.asList(
encodedPhoneticFamilyName, encodedPhoneticMiddleName,
encodedPhoneticGivenName))) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(encodedPhoneticFamilyName);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(escapeCharacters(phoneticMiddleName));
builder.append(encodedPhoneticGivenName);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(escapeCharacters(phoneticGivenName));
builder.append(encodedPhoneticMiddleName);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_COL_SEPARATOR);
@ -939,21 +1002,72 @@ public class VCardComposer {
if (mUsesDefactProperty) {
if (!TextUtils.isEmpty(phoneticGivenName)) {
final boolean reallyUseQuotedPrintable =
(mUsesQPToPrimaryProperties &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName));
final String encodedPhoneticGivenName;
if (reallyUseQuotedPrintable) {
encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName);
} else {
encodedPhoneticGivenName = escapeCharacters(phoneticGivenName);
}
builder.append(VCARD_PROPERTY_X_PHONETIC_FIRST_NAME);
if (shouldAppendCharsetAttribute(encodedPhoneticGivenName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
if (reallyUseQuotedPrintable) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(VCARD_ATTR_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(phoneticGivenName);
builder.append(encodedPhoneticGivenName);
builder.append(VCARD_COL_SEPARATOR);
}
if (!TextUtils.isEmpty(phoneticMiddleName)) {
final boolean reallyUseQuotedPrintable =
(mUsesQPToPrimaryProperties &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName));
final String encodedPhoneticMiddleName;
if (reallyUseQuotedPrintable) {
encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName);
} else {
encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName);
}
builder.append(VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME);
if (shouldAppendCharsetAttribute(encodedPhoneticMiddleName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
if (reallyUseQuotedPrintable) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(VCARD_ATTR_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(phoneticMiddleName);
builder.append(encodedPhoneticMiddleName);
builder.append(VCARD_COL_SEPARATOR);
}
if (!TextUtils.isEmpty(phoneticFamilyName)) {
final boolean reallyUseQuotedPrintable =
(mUsesQPToPrimaryProperties &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName));
final String encodedPhoneticFamilyName;
if (reallyUseQuotedPrintable) {
encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName);
} else {
encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName);
}
builder.append(VCARD_PROPERTY_X_PHONETIC_LAST_NAME);
if (shouldAppendCharsetAttribute(encodedPhoneticFamilyName)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
if (reallyUseQuotedPrintable) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(VCARD_ATTR_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(phoneticFamilyName);
builder.append(encodedPhoneticFamilyName);
builder.append(VCARD_COL_SEPARATOR);
}
}
@ -975,21 +1089,32 @@ public class VCardComposer {
}
for (ContentValues contentValues : contentValuesList) {
final String nickname = contentValues
.getAsString(Nickname.NAME);
final String nickname = contentValues.getAsString(Nickname.NAME);
if (TextUtils.isEmpty(nickname)) {
continue;
}
builder.append(propertyNickname);
if (!VCardUtils.containsOnlyPrintableAscii(propertyNickname)) {
// Strictly, this is not valid in vCard 3.0. See above.
final String encodedNickname;
final boolean reallyUseQuotedPrintable =
(mUsesQuotedPrintable &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(nickname));
if (reallyUseQuotedPrintable) {
encodedNickname = encodeQuotedPrintable(nickname);
} else {
encodedNickname = escapeCharacters(nickname);
}
builder.append(propertyNickname);
if (shouldAppendCharsetAttribute(propertyNickname)) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(mVCardAttributeCharset);
}
if (reallyUseQuotedPrintable) {
builder.append(VCARD_ATTR_SEPARATOR);
builder.append(VCARD_ATTR_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
builder.append(escapeCharacters(nickname));
builder.append(encodedNickname);
builder.append(VCARD_COL_SEPARATOR);
}
}
@ -1048,7 +1173,6 @@ public class VCardComposer {
continue;
}
emailAddressExists = true;
// Do not allow completely same email address line emitted into each file.
if (!addressSet.contains(emailAddress)) {
addressSet.add(emailAddress);
appendVCardEmailLine(builder, type, label, emailAddress);
@ -1065,7 +1189,6 @@ public class VCardComposer {
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(StructuredPostal.CONTENT_ITEM_TYPE);
if (contentValuesList != null) {
if (mIsDoCoMo) {
appendPostalsForDoCoMo(builder, contentValuesList);
@ -1082,7 +1205,7 @@ public class VCardComposer {
}
/**
* Try to append just one line. If there's no appropriate address
* Tries to append just one line. If there's no appropriate address
* information, append an empty line.
*/
private void appendPostalsForDoCoMo(final StringBuilder builder,
@ -1751,6 +1874,35 @@ public class VCardComposer {
builder.append(type);
}
/**
* Returns true when the property line should contain charset attribute
* information. This method may return true even when vCard version is 3.0.
*
* Strictly, adding charset information is invalid in VCard 3.0.
* However we'll add the info only when used charset is not UTF-8
* in vCard 3.0 format, since parser side may be able to use the charset
* via this field, though we may encounter another problem by adding it...
*
* e.g. Japanese mobile phones use Shift_Jis while RFC 2426
* recommends UTF-8. By adding this field, parsers may be able
* to know this text is NOT UTF-8 but Shift_Jis.
*/
private boolean shouldAppendCharsetAttribute(final String propertyValue) {
return (!VCardUtils.containsOnlyPrintableAscii(propertyValue) &&
(!mIsV30 || !mUsesUtf8));
}
private boolean shouldAppendCharsetAttribute(final List<String> propertyValueList) {
boolean shouldAppendBasically = false;
for (String propertyValue : propertyValueList) {
if (!VCardUtils.containsOnlyPrintableAscii(propertyValue)) {
shouldAppendBasically = true;
break;
}
}
return shouldAppendBasically && (!mIsV30 || !mUsesUtf8);
}
private String encodeQuotedPrintable(String str) {
if (TextUtils.isEmpty(str)) {
return "";

View File

@ -55,7 +55,7 @@ public class VCardConfig {
private static final int FLAG_CHARSET_UTF8 = 0;
private static final int FLAG_CHARSET_SHIFT_JIS = 0x20;
/**
* The flag indicating the vCard composer will add some "X-" properties used only in Android
* when the formal vCard specification does not have appropriate fields for that data.
@ -85,7 +85,7 @@ public class VCardConfig {
* in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties.
*/
private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000;
/**
* The flag indicating some specific dialect seen in vcard of DoCoMo (one of Japanese
* mobile careers) should be used. This flag does not include any other information like
@ -94,10 +94,20 @@ public class VCardConfig {
*/
private static final int FLAG_DOCOMO = 0x20000000;
/**
* The flag indicating the vCard composer use Quoted-Printable toward even "primary" types.
* In this context, "primary" types means "N", "FN", etc. which are usually "not" encoded
* into Quoted-Printable format in external exporters.
* This flag is useful when some target importer does not accept "primary" property values
* without Quoted-Printable encoding.
*
* @hide Temporaly made public. We don't strictly define "primary", so we may change the
* behavior around this flag in the future. Do not use this flag without any reason.
*/
public static final int FLAG_USE_QP_TO_PRIMARY_PROPERTIES = 0x10000000;
// VCard types
/**
* General vCard format with the version 2.1. Uses UTF-8 for the charset.
* When composing a vCard entry, the US convension will be used.
@ -114,10 +124,10 @@ public class VCardConfig {
/**
* General vCard format with the version 3.0. Uses UTF-8 for the charset.
*
* Note that this type is not fully implemented, so probably some bugs remain especially
* in parsing part.
*
* TODO: implement this type.
* Note that this type is not fully implemented, so probably some bugs remain both in
* parsing and composing.
*
* TODO: implement this type correctly.
*/
public static final int VCARD_TYPE_V30_GENERIC =
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
@ -243,6 +253,10 @@ public class VCardConfig {
(vcardType == VCARD_TYPE_DOCOMO));
}
public static boolean usesUtf8(int vcardType) {
return ((vcardType & FLAG_CHARSET_UTF8) != 0);
}
public static boolean usesShiftJis(int vcardType) {
return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0);
}
@ -257,7 +271,7 @@ public class VCardConfig {
public static boolean needsToConvertPhoneticString(int vcardType) {
return (vcardType == VCARD_TYPE_DOCOMO);
}
public static int getNameOrderType(int vcardType) {
return vcardType & NAME_ORDER_MASK;
}
@ -265,19 +279,27 @@ public class VCardConfig {
public static boolean usesAndroidSpecificProperty(int vcardType) {
return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0);
}
public static boolean usesDefactProperty(int vcardType) {
return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0);
}
public static boolean onlyOneNoteFieldIsAvailable(int vcardType) {
return vcardType == VCARD_TYPE_DOCOMO;
}
public static boolean showPerformanceLog() {
return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
}
/**
* @hide
*/
public static boolean usesQPToPrimaryProperties(int vcardType) {
return (usesQuotedPrintable(vcardType) &&
((vcardType & FLAG_USE_QP_TO_PRIMARY_PROPERTIES) != 0));
}
private VCardConfig() {
}
}