Merge change I9195a354 into eclair-mr2

* changes:
  Add partial support of Android-specific properties.
This commit is contained in:
Android (Google) Code Review
2009-10-28 21:54:29 -04:00
4 changed files with 192 additions and 33 deletions

View File

@@ -72,6 +72,9 @@ package android.pim.vcard;
// 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 @@ package android.pim.vcard;
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() {
}
}

View File

@@ -442,7 +442,8 @@ public class ContactStruct {
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 @@ public class ContactStruct {
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 @@ public class ContactStruct {
}
}
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 @@ public class ContactStruct {
mDisplayName = "";
}
}
/**
* Consolidate several fielsds (like mName) using name candidates,
*/
@@ -1028,7 +1041,7 @@ public class ContactStruct {
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 @@ public class ContactStruct {
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);

View File

@@ -1019,11 +1019,11 @@ public class VCardComposer {
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 @@ public class VCardComposer {
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 @@ public class VCardComposer {
}
}
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 @@ public class VCardComposer {
}
}
// 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 @@ public class VCardComposer {
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.
*/

View File

@@ -643,9 +643,6 @@ public class VCardExporterTests extends AndroidTestCase {
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 @@ public class VCardExporterTests extends AndroidTestCase {
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);
}
}