Merge change I9195a354 into eclair-mr2
* changes:
Add partial support of Android-specific properties.
diff --git a/core/java/android/pim/vcard/Constants.java b/core/java/android/pim/vcard/Constants.java
index a1c7e10..052f329 100644
--- a/core/java/android/pim/vcard/Constants.java
+++ b/core/java/android/pim/vcard/Constants.java
@@ -72,6 +72,9 @@
// Phone number for Skype, available as usual phone.
public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
+ // Property for Android-specific fields.
+ public static final String PROPERTY_X_ANDROID_CUSTOM = "X-ANDROID-CUSTOM";
+
// Properties for DoCoMo vCard.
public static final String PROPERTY_X_CLASS = "X-CLASS";
public static final String PROPERTY_X_REDUCTION = "X-REDUCTION";
@@ -158,6 +161,9 @@
public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
}
+ // TODO: Should be in ContactsContract?
+ /* package */ static final int MAX_DATA_COLUMN = 15;
+
private Constants() {
}
}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/ContactStruct.java b/core/java/android/pim/vcard/ContactStruct.java
index eb9c48a..9d40db6 100644
--- a/core/java/android/pim/vcard/ContactStruct.java
+++ b/core/java/android/pim/vcard/ContactStruct.java
@@ -442,7 +442,8 @@
private List<ImData> mImList;
private List<PhotoData> mPhotoList;
private List<String> mWebsiteList;
-
+ private List<List<String>> mAndroidCustomPropertyList;
+
private final int mVCardType;
private final Account mAccount;
@@ -928,14 +929,19 @@
mWebsiteList = new ArrayList<String>(1);
}
mWebsiteList.add(propValue);
+ } else if (propName.equals(Constants.PROPERTY_BDAY)) {
+ mBirthday = propValue;
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
mPhoneticGivenName = propValue;
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
mPhoneticMiddleName = propValue;
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_LAST_NAME)) {
mPhoneticFamilyName = propValue;
- } else if (propName.equals(Constants.PROPERTY_BDAY)) {
- mBirthday = propValue;
+ } else if (propName.equals(Constants.PROPERTY_X_ANDROID_CUSTOM)) {
+ final List<String> customPropertyList =
+ VCardUtils.constructListFromValue(propValue,
+ VCardConfig.isV30(mVCardType));
+ handleAndroidCustomProperty(customPropertyList);
/*} else if (propName.equals("REV")) {
// Revision of this VCard entry. I think we can ignore this.
} else if (propName.equals("UID")) {
@@ -963,6 +969,13 @@
}
}
+ private void handleAndroidCustomProperty(final List<String> customPropertyList) {
+ if (mAndroidCustomPropertyList == null) {
+ mAndroidCustomPropertyList = new ArrayList<List<String>>();
+ }
+ mAndroidCustomPropertyList.add(customPropertyList);
+ }
+
/**
* Construct the display name. The constructed data must not be null.
*/
@@ -1017,7 +1030,7 @@
mDisplayName = "";
}
}
-
+
/**
* Consolidate several fielsds (like mName) using name candidates,
*/
@@ -1028,7 +1041,7 @@
mPhoneticFullName = mPhoneticFullName.trim();
}
}
-
+
// From GoogleSource.java in Contacts app.
private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
@@ -1224,6 +1237,36 @@
operationList.add(builder.build());
}
+ if (mAndroidCustomPropertyList != null) {
+ for (List<String> customPropertyList : mAndroidCustomPropertyList) {
+ int size = customPropertyList.size();
+ if (size < 2 || TextUtils.isEmpty(customPropertyList.get(0))) {
+ continue;
+ } else if (size > Constants.MAX_DATA_COLUMN + 1) {
+ size = Constants.MAX_DATA_COLUMN + 1;
+ customPropertyList =
+ customPropertyList.subList(0, Constants.MAX_DATA_COLUMN + 2);
+ }
+
+ int i = 0;
+ for (final String customPropertyValue : customPropertyList) {
+ if (i == 0) {
+ final String mimeType = customPropertyValue;
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, mimeType);
+ } else { // 1 <= i && i <= MAX_DATA_COLUMNS
+ if (!TextUtils.isEmpty(customPropertyValue)) {
+ builder.withValue("data" + i, customPropertyValue);
+ }
+ }
+
+ operationList.add(builder.build());
+ i++;
+ }
+ }
+ }
+
if (myGroupsId != null) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0);
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index 8eabd4b1..33581c9 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -1019,11 +1019,11 @@
return;
}
- final String propertyNickname;
+ final boolean useAndroidProperty;
if (mIsV30) {
- propertyNickname = Constants.PROPERTY_NICKNAME;
- /*} else if (mUsesAndroidProperty) {
- propertyNickname = VCARD_PROPERTY_X_NICKNAME;*/
+ useAndroidProperty = false;
+ } else if (mUsesAndroidProperty) {
+ useAndroidProperty = true;
} else {
// There's no way to add this field.
return;
@@ -1034,29 +1034,13 @@
if (TextUtils.isEmpty(nickname)) {
continue;
}
-
- final String encodedNickname;
- final boolean reallyUseQuotedPrintable =
- (mUsesQuotedPrintable &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(nickname));
- if (reallyUseQuotedPrintable) {
- encodedNickname = encodeQuotedPrintable(nickname);
+ if (useAndroidProperty) {
+ appendAndroidSpecificProperty(builder, Nickname.CONTENT_ITEM_TYPE,
+ contentValues);
} else {
- encodedNickname = escapeCharacters(nickname);
+ appendVCardLineWithCharsetAndQPDetection(builder,
+ Constants.PROPERTY_NICKNAME, 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(encodedNickname);
- builder.append(VCARD_COL_SEPARATOR);
}
}
@@ -1491,6 +1475,33 @@
}
}
+ private void appendAndroidSpecificProperty(final StringBuilder builder,
+ final String mimeType, ContentValues contentValues) {
+ List<String> rawDataList = new ArrayList<String>();
+ rawDataList.add(mimeType);
+ final List<String> columnNameList;
+ if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
+
+ } else {
+ // If you add the other field, please check all the columns are able to be
+ // converted to String.
+ //
+ // e.g. BLOB is not what we can handle here now.
+ return;
+ }
+
+ for (int i = 0; i < Constants.MAX_DATA_COLUMN; i++) {
+ String value = contentValues.getAsString("data" + i);
+ if (value == null) {
+ value = "";
+ }
+ rawDataList.add(value);
+ }
+
+ appendVCardLineWithCharsetAndQPDetection(builder,
+ Constants.PROPERTY_X_ANDROID_CUSTOM, rawDataList);
+ }
+
/**
* Append '\' to the characters which should be escaped. The character set is different
* not only between vCard 2.1 and vCard 3.0 but also among each device.
@@ -1968,6 +1979,8 @@
}
}
+ // appendVCardLine() variants accepting one String.
+
private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
final String propertyName, final String rawData) {
appendVCardLineWithCharsetAndQPDetection(builder, propertyName, null, rawData);
@@ -2026,6 +2039,87 @@
builder.append(VCARD_COL_SEPARATOR);
}
+ // appendVCardLine() variants accepting List<String>.
+
+ private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
+ final String propertyName, final List<String> rawDataList) {
+ appendVCardLineWithCharsetAndQPDetection(builder, propertyName, null, rawDataList);
+ }
+
+ private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
+ final String propertyName,
+ final List<String> attributeList, final List<String> rawDataList) {
+ boolean needCharset = false;
+ boolean reallyUseQuotedPrintable = false;
+ for (String rawData : rawDataList) {
+ if (!needCharset && mUsesQuotedPrintable &&
+ !VCardUtils.containsOnlyPrintableAscii(rawData)) {
+ needCharset = true;
+ }
+ if (!reallyUseQuotedPrintable &&
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawData)) {
+ reallyUseQuotedPrintable = true;
+ }
+ if (needCharset && reallyUseQuotedPrintable) {
+ break;
+ }
+ }
+
+ appendVCardLine(builder, propertyName, attributeList,
+ rawDataList, needCharset, reallyUseQuotedPrintable);
+ }
+
+ /*
+ private void appendVCardLine(final StringBuilder builder,
+ final String propertyName, final List<String> rawDataList) {
+ appendVCardLine(builder, propertyName, rawDataList, false, false);
+ }
+
+ private void appendVCardLine(final StringBuilder builder,
+ final String propertyName, final List<String> rawDataList,
+ final boolean needCharset, boolean needQuotedPrintable) {
+ appendVCardLine(builder, propertyName, null, rawDataList, needCharset, needQuotedPrintable);
+ }*/
+
+ private void appendVCardLine(final StringBuilder builder,
+ final String propertyName,
+ final List<String> attributeList,
+ final List<String> rawDataList, final boolean needCharset,
+ boolean needQuotedPrintable) {
+ builder.append(propertyName);
+ if (attributeList != null && attributeList.size() > 0) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ appendTypeAttributes(builder, attributeList);
+ }
+ if (needCharset) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ boolean first = true;
+ for (String rawData : rawDataList) {
+ final String encodedData;
+ if (needQuotedPrintable) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(VCARD_ATTR_ENCODING_QP);
+ encodedData = encodeQuotedPrintable(rawData);
+ } else {
+ // TODO: one line may be too huge, which may be invalid in vCard spec, though
+ // several (even well-known) applications do not care this.
+ encodedData = escapeCharacters(rawData);
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ builder.append(VCARD_ITEM_SEPARATOR);
+ }
+ builder.append(encodedData);
+ }
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
/**
* VCARD_ATTR_SEPARATOR must be appended before this method being called.
*/
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java
index d0097c4..f1a46d6 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java
@@ -643,9 +643,6 @@
testStructuredNameUseSuperPrimaryCommon(V30);
}
- /**
- * There's no property for nickname in vCard 2.1, so we don't have any requirement on it.
- */
public void testNickNameV30() {
ExportTestResolver resolver = new ExportTestResolver();
ContentValues contentValues = resolver.buildData(Nickname.CONTENT_ITEM_TYPE);
@@ -1269,4 +1266,23 @@
verifyOneComposition(resolver, handler, version);
}
+
+ /**
+ * There's no "NICKNAME" property in vCard 2.1, while there is in vCard 3.0.
+ * We use Android-specific "X-ANDROID-CUSTOM" property.
+ * This test verifies the functionality.
+ */
+ public void testNickNameV21() {
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(Nickname.CONTENT_ITEM_TYPE);
+ contentValues.put(Nickname.NAME, "Nicky");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, V21);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("X-ANDROID-CUSTOM", Nickname.CONTENT_ITEM_TYPE + ";Nicky");
+
+ // TODO: also test import part.
+
+ verifyOneComposition(resolver, handler, V21);
+ }
}