Merge change I9195a354 into eclair-mr2
* changes: Add partial support of Android-specific properties.
This commit is contained in:
@@ -72,6 +72,9 @@ package android.pim.vcard;
|
|||||||
// Phone number for Skype, available as usual phone.
|
// Phone number for Skype, available as usual phone.
|
||||||
public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
|
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.
|
// Properties for DoCoMo vCard.
|
||||||
public static final String PROPERTY_X_CLASS = "X-CLASS";
|
public static final String PROPERTY_X_CLASS = "X-CLASS";
|
||||||
public static final String PROPERTY_X_REDUCTION = "X-REDUCTION";
|
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";
|
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() {
|
private Constants() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,6 +442,7 @@ public class ContactStruct {
|
|||||||
private List<ImData> mImList;
|
private List<ImData> mImList;
|
||||||
private List<PhotoData> mPhotoList;
|
private List<PhotoData> mPhotoList;
|
||||||
private List<String> mWebsiteList;
|
private List<String> mWebsiteList;
|
||||||
|
private List<List<String>> mAndroidCustomPropertyList;
|
||||||
|
|
||||||
private final int mVCardType;
|
private final int mVCardType;
|
||||||
private final Account mAccount;
|
private final Account mAccount;
|
||||||
@@ -928,14 +929,19 @@ public class ContactStruct {
|
|||||||
mWebsiteList = new ArrayList<String>(1);
|
mWebsiteList = new ArrayList<String>(1);
|
||||||
}
|
}
|
||||||
mWebsiteList.add(propValue);
|
mWebsiteList.add(propValue);
|
||||||
|
} else if (propName.equals(Constants.PROPERTY_BDAY)) {
|
||||||
|
mBirthday = propValue;
|
||||||
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
|
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
|
||||||
mPhoneticGivenName = propValue;
|
mPhoneticGivenName = propValue;
|
||||||
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
|
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
|
||||||
mPhoneticMiddleName = propValue;
|
mPhoneticMiddleName = propValue;
|
||||||
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_LAST_NAME)) {
|
} else if (propName.equals(Constants.PROPERTY_X_PHONETIC_LAST_NAME)) {
|
||||||
mPhoneticFamilyName = propValue;
|
mPhoneticFamilyName = propValue;
|
||||||
} else if (propName.equals(Constants.PROPERTY_BDAY)) {
|
} else if (propName.equals(Constants.PROPERTY_X_ANDROID_CUSTOM)) {
|
||||||
mBirthday = propValue;
|
final List<String> customPropertyList =
|
||||||
|
VCardUtils.constructListFromValue(propValue,
|
||||||
|
VCardConfig.isV30(mVCardType));
|
||||||
|
handleAndroidCustomProperty(customPropertyList);
|
||||||
/*} else if (propName.equals("REV")) {
|
/*} else if (propName.equals("REV")) {
|
||||||
// Revision of this VCard entry. I think we can ignore this.
|
// Revision of this VCard entry. I think we can ignore this.
|
||||||
} else if (propName.equals("UID")) {
|
} 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.
|
* Construct the display name. The constructed data must not be null.
|
||||||
*/
|
*/
|
||||||
@@ -1224,6 +1237,36 @@ public class ContactStruct {
|
|||||||
operationList.add(builder.build());
|
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) {
|
if (myGroupsId != null) {
|
||||||
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
|
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
|
||||||
builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0);
|
builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0);
|
||||||
|
|||||||
@@ -1019,11 +1019,11 @@ public class VCardComposer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String propertyNickname;
|
final boolean useAndroidProperty;
|
||||||
if (mIsV30) {
|
if (mIsV30) {
|
||||||
propertyNickname = Constants.PROPERTY_NICKNAME;
|
useAndroidProperty = false;
|
||||||
/*} else if (mUsesAndroidProperty) {
|
} else if (mUsesAndroidProperty) {
|
||||||
propertyNickname = VCARD_PROPERTY_X_NICKNAME;*/
|
useAndroidProperty = true;
|
||||||
} else {
|
} else {
|
||||||
// There's no way to add this field.
|
// There's no way to add this field.
|
||||||
return;
|
return;
|
||||||
@@ -1034,29 +1034,13 @@ public class VCardComposer {
|
|||||||
if (TextUtils.isEmpty(nickname)) {
|
if (TextUtils.isEmpty(nickname)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (useAndroidProperty) {
|
||||||
final String encodedNickname;
|
appendAndroidSpecificProperty(builder, Nickname.CONTENT_ITEM_TYPE,
|
||||||
final boolean reallyUseQuotedPrintable =
|
contentValues);
|
||||||
(mUsesQuotedPrintable &&
|
|
||||||
!VCardUtils.containsOnlyNonCrLfPrintableAscii(nickname));
|
|
||||||
if (reallyUseQuotedPrintable) {
|
|
||||||
encodedNickname = encodeQuotedPrintable(nickname);
|
|
||||||
} else {
|
} 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
|
* 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.
|
* 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,
|
private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
|
||||||
final String propertyName, final String rawData) {
|
final String propertyName, final String rawData) {
|
||||||
appendVCardLineWithCharsetAndQPDetection(builder, propertyName, null, rawData);
|
appendVCardLineWithCharsetAndQPDetection(builder, propertyName, null, rawData);
|
||||||
@@ -2026,6 +2039,87 @@ public class VCardComposer {
|
|||||||
builder.append(VCARD_COL_SEPARATOR);
|
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.
|
* VCARD_ATTR_SEPARATOR must be appended before this method being called.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -643,9 +643,6 @@ public class VCardExporterTests extends AndroidTestCase {
|
|||||||
testStructuredNameUseSuperPrimaryCommon(V30);
|
testStructuredNameUseSuperPrimaryCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* There's no property for nickname in vCard 2.1, so we don't have any requirement on it.
|
|
||||||
*/
|
|
||||||
public void testNickNameV30() {
|
public void testNickNameV30() {
|
||||||
ExportTestResolver resolver = new ExportTestResolver();
|
ExportTestResolver resolver = new ExportTestResolver();
|
||||||
ContentValues contentValues = resolver.buildData(Nickname.CONTENT_ITEM_TYPE);
|
ContentValues contentValues = resolver.buildData(Nickname.CONTENT_ITEM_TYPE);
|
||||||
@@ -1269,4 +1266,23 @@ public class VCardExporterTests extends AndroidTestCase {
|
|||||||
|
|
||||||
verifyOneComposition(resolver, handler, version);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user