summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/pim/vcard/Constants.java46
-rw-r--r--core/java/android/pim/vcard/ContactStruct.java57
-rw-r--r--core/java/android/pim/vcard/VCardComposer.java839
-rw-r--r--core/java/android/pim/vcard/VCardConfig.java292
-rw-r--r--core/java/android/pim/vcard/VCardDataBuilder.java2
-rw-r--r--core/java/android/pim/vcard/VCardUtils.java67
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java2
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java876
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java41
9 files changed, 1733 insertions, 489 deletions
diff --git a/core/java/android/pim/vcard/Constants.java b/core/java/android/pim/vcard/Constants.java
index aaa7215..a1c7e10 100644
--- a/core/java/android/pim/vcard/Constants.java
+++ b/core/java/android/pim/vcard/Constants.java
@@ -56,7 +56,7 @@ package android.pim.vcard;
public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
- // Properties both the current (as of 2009-08-17) ContactsStruct and de-fact vCard extensions
+ // Properties both ContactsStruct in Eclair and de-fact vCard extensions
// shown in http://en.wikipedia.org/wiki/VCard support are defined here.
public static final String PROPERTY_X_AIM = "X-AIM";
public static final String PROPERTY_X_MSN = "X-MSN";
@@ -65,15 +65,12 @@ package android.pim.vcard;
public static final String PROPERTY_X_JABBER = "X-JABBER";
public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK";
public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME";
+ // Properties only ContactsStruct has. We alse use this.
+ public static final String PROPERTY_X_QQ = "X-QQ";
+ public static final String PROPERTY_X_NETMEETING = "X-NETMEETING";
+
// Phone number for Skype, available as usual phone.
public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
- // Some device emits this "X-" attribute, which is specifically invalid but should be
- // always properly accepted, and emitted in some special case (for that device/application).
- public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
-
- // Android specific properties
- // Use only in vCard paser code.
- public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
// Properties for DoCoMo vCard.
public static final String PROPERTY_X_CLASS = "X-CLASS";
@@ -81,6 +78,11 @@ package android.pim.vcard;
public static final String PROPERTY_X_NO = "X-NO";
public static final String PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE";
+ // For some historical reason, we often use the term "ATTR"/"attribute" especially toward
+ // what is called "param" in both vCard specs, while vCard, while vCard importer side uses
+ // "param".
+ //
+ // TODO: Confusing. Fix it.
public static final String ATTR_TYPE = "TYPE";
// How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0
@@ -102,13 +104,19 @@ package android.pim.vcard;
public static final String ATTR_TYPE_VOICE = "VOICE";
public static final String ATTR_TYPE_INTERNET = "INTERNET";
- // Abbreviation of "preferable"? We interpret this value as "primary" property.
+ // Abbreviation of "prefered" according to vCard 2.1 specification.
+ // We interpret this value as "primary" property during import/export.
+ //
+ // Note: Both vCard specs does anything about the requirement about this attribute,
+ // but there may be some vCard importer which will get confused with more than
+ // one "PREF"s in one property name, while Android accepts them.
public static final String ATTR_TYPE_PREF = "PREF";
// Phone types valid in vCard and known to ContactsContract, but not so common.
public static final String ATTR_TYPE_CAR = "CAR";
public static final String ATTR_TYPE_ISDN = "ISDN";
public static final String ATTR_TYPE_PAGER = "PAGER";
+ public static final String ATTR_TYPE_TLX = "TLX"; // Telex
// Phone types existing in vCard 2.1 but not known to ContactsContract.
// TODO: should make parser make these TYPE_CUSTOM.
@@ -118,16 +126,16 @@ package android.pim.vcard;
public static final String ATTR_TYPE_VIDEO = "VIDEO";
// Attribute for Phones, which are not formally valid in vCard (at least 2.1).
- // These types are encoded to "X-" attributes when composing vCard for now.
- // Parser passes these even if "X-" is added to the attribute.
- public static final String ATTR_PHONE_EXTRA_TYPE_OTHER = "OTHER";
+ // These types are basically encoded to "X-" attributes when composing vCard.
+ // Parser passes these when "X-" is added to the attribute or not.
public static final String ATTR_PHONE_EXTRA_TYPE_CALLBACK = "CALLBACK";
- // TODO: may be "TYPE=COMPANY,PREF", not "COMPANY-MAIN".
- public static final String ATTR_PHONE_EXTRA_TYPE_COMPANY_MAIN = "COMPANY-MAIN";
public static final String ATTR_PHONE_EXTRA_TYPE_RADIO = "RADIO";
- public static final String ATTR_PHONE_EXTRA_TYPE_TELEX = "TELEX";
public static final String ATTR_PHONE_EXTRA_TYPE_TTY_TDD = "TTY-TDD";
public static final String ATTR_PHONE_EXTRA_TYPE_ASSISTANT = "ASSISTANT";
+ // vCard composer translates this type to "WORK" + "PREF". Just for parsing.
+ public static final String ATTR_PHONE_EXTRA_TYPE_COMPANY_MAIN = "COMPANY-MAIN";
+ // vCard composer translates this type to "VOICE" Just for parsing.
+ public static final String ATTR_PHONE_EXTRA_TYPE_OTHER = "OTHER";
// Attribute for addresses.
public static final String ATTR_ADR_TYPE_PARCEL = "PARCEL";
@@ -142,6 +150,14 @@ package android.pim.vcard;
// vCard 3.0.
public static final String ATTR_TYPE_X_IRMC_N = "X-IRMC-N";
+ public interface ImportOnly {
+ public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
+ // Some device emits this "X-" attribute for expressing Google Talk,
+ // which is specifically invalid but should be always properly accepted, and emitted
+ // in some special case (for that device/application).
+ public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
+ }
+
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 046fb02..eb9c48a 100644
--- a/core/java/android/pim/vcard/ContactStruct.java
+++ b/core/java/android/pim/vcard/ContactStruct.java
@@ -68,7 +68,8 @@ public class ContactStruct {
sImMap.put(Constants.PROPERTY_X_JABBER, Im.PROTOCOL_JABBER);
sImMap.put(Constants.PROPERTY_X_SKYPE_USERNAME, Im.PROTOCOL_SKYPE);
sImMap.put(Constants.PROPERTY_X_GOOGLE_TALK, Im.PROTOCOL_GOOGLE_TALK);
- sImMap.put(Constants.PROPERTY_X_GOOGLE_TALK_WITH_SPACE, Im.PROTOCOL_GOOGLE_TALK);
+ sImMap.put(Constants.ImportOnly.PROPERTY_X_GOOGLE_TALK_WITH_SPACE,
+ Im.PROTOCOL_GOOGLE_TALK);
}
static public class PhoneData {
@@ -292,16 +293,18 @@ public class ContactStruct {
}
static public class ImData {
+ public final int protocol;
+ public final String customProtocol;
public final int type;
public final String data;
- public final String label;
public final boolean isPrimary;
- // TODO: ContactsConstant#PROTOCOL, ContactsConstant#CUSTOM_PROTOCOL should be used?
- public ImData(int type, String data, String label, boolean isPrimary) {
+ public ImData(int protocol, String customProtocol, int type,
+ String data, boolean isPrimary) {
+ this.protocol = protocol;
+ this.customProtocol = customProtocol;
this.type = type;
this.data = data;
- this.label = label;
this.isPrimary = isPrimary;
}
@@ -311,14 +314,18 @@ public class ContactStruct {
return false;
}
ImData imData = (ImData)obj;
- return (type == imData.type && data.equals(imData.data) &&
- label.equals(imData.label) && isPrimary == imData.isPrimary);
+ return (type == imData.type && protocol == imData.protocol
+ && (customProtocol != null ? customProtocol.equals(imData.customProtocol) :
+ (imData.customProtocol == null))
+ && (data != null ? data.equals(imData.data) : (imData.data == null))
+ && isPrimary == imData.isPrimary);
}
@Override
public String toString() {
- return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
- type, data, label, isPrimary);
+ return String.format(
+ "type: %d, protocol: %d, custom_protcol: %s, data: %s, isPrimary: %s",
+ type, protocol, customProtocol, data, isPrimary);
}
}
@@ -440,7 +447,7 @@ public class ContactStruct {
private final Account mAccount;
public ContactStruct() {
- this(VCardConfig.VCARD_TYPE_V21_GENERIC);
+ this(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
}
public ContactStruct(int vcardType) {
@@ -619,11 +626,12 @@ public class ContactStruct {
addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false);
}
- private void addIm(int type, String data, String label, boolean isPrimary) {
+ private void addIm(int protocol, String customProtocol, int type,
+ String propValue, boolean isPrimary) {
if (mImList == null) {
mImList = new ArrayList<ImData>();
}
- mImList.add(new ImData(type, data, label, isPrimary));
+ mImList.add(new ImData(protocol, customProtocol, type, propValue, isPrimary));
}
private void addNote(final String note) {
@@ -720,7 +728,7 @@ public class ContactStruct {
} else if (propName.equals(Constants.PROPERTY_NICKNAME)) {
mPhoneticFullName = propValue;
} else if (propName.equals(Constants.PROPERTY_NICKNAME) ||
- propName.equals(Constants.PROPERTY_X_NICKNAME)) {
+ propName.equals(Constants.ImportOnly.PROPERTY_X_NICKNAME)) {
addNickName(propValue);
} else if (propName.equals(Constants.PROPERTY_SOUND)) {
Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
@@ -891,25 +899,28 @@ public class ContactStruct {
isPrimary = false;
}
addPhone(type, propValue, label, isPrimary);
- } else if (sImMap.containsKey(propName)){
- int type = sImMap.get(propName);
+ } else if (sImMap.containsKey(propName)) {
+ final int protocol = sImMap.get(propName);
boolean isPrimary = false;
+ int type = -1;
final Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
if (typeCollection != null) {
for (String typeString : typeCollection) {
if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
isPrimary = true;
- } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_HOME)) {
- type = Phone.TYPE_HOME;
- } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_WORK)) {
- type = Phone.TYPE_WORK;
+ } else if (type < 0) {
+ if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_HOME)) {
+ type = Im.TYPE_HOME;
+ } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_WORK)) {
+ type = Im.TYPE_WORK;
+ }
}
}
}
if (type < 0) {
type = Phone.TYPE_HOME;
}
- addIm(type, propValue, null, isPrimary);
+ addIm(protocol, null, type, propValue, isPrimary);
} else if (propName.equals(Constants.PROPERTY_NOTE)) {
addNote(propValue);
} else if (propName.equals(Constants.PROPERTY_URL)) {
@@ -1158,10 +1169,10 @@ public class ContactStruct {
builder.withValueBackReference(Im.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
builder.withValue(Im.TYPE, imData.type);
- if (imData.type == Im.TYPE_CUSTOM) {
- builder.withValue(Im.LABEL, imData.label);
+ builder.withValue(Im.PROTOCOL, imData.protocol);
+ if (imData.protocol == Im.PROTOCOL_CUSTOM) {
+ builder.withValue(Im.CUSTOM_PROTOCOL, imData.customProtocol);
}
- builder.withValue(Im.DATA, imData.data);
if (imData.isPrimary) {
builder.withValue(Data.IS_PRIMARY, 1);
}
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index 9cb9c90..c5afb73 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -100,7 +100,10 @@ import java.util.Set;
public class VCardComposer {
private static final String LOG_TAG = "vcard.VCardComposer";
- private static final String DEFAULT_EMAIL_TYPE = Constants.ATTR_TYPE_INTERNET;
+ // TODO: Should be configurable?
+ public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME;
+ public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME;
+ public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER;
public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
"Failed to get database information";
@@ -154,6 +157,11 @@ public class VCardComposer {
private static final Uri sDataRequestUri;
private static final Map<Integer, String> sImMap;
+ /**
+ * See the comment in {@link VCardConfig#FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES}.
+ */
+ private static final Set<String> sPrimaryPropertyNameSet;
+
static {
Uri.Builder builder = RawContacts.CONTENT_URI.buildUpon();
builder.appendQueryParameter(Data.FOR_EXPORT_ONLY, "1");
@@ -166,6 +174,12 @@ public class VCardComposer {
sImMap.put(Im.PROTOCOL_JABBER, Constants.PROPERTY_X_JABBER);
sImMap.put(Im.PROTOCOL_SKYPE, Constants.PROPERTY_X_SKYPE_USERNAME);
// Google talk is a special case.
+
+ // TODO: incomplete. Implement properly
+ sPrimaryPropertyNameSet = new HashSet<String>();
+ sPrimaryPropertyNameSet.add(Constants.PROPERTY_N);
+ sPrimaryPropertyNameSet.add(Constants.PROPERTY_FN);
+ sPrimaryPropertyNameSet.add(Constants.PROPERTY_SOUND);
}
public static interface OneEntryHandler {
@@ -287,8 +301,9 @@ public class VCardComposer {
private final boolean mUsesDefactProperty;
private final boolean mUsesUtf8;
private final boolean mUsesShiftJis;
- private final boolean mUsesQPToPrimaryProperties;
private final boolean mAppendTypeParamName;
+ private final boolean mRefrainsQPToPrimaryProperties;
+ private final boolean mNeedsToConvertPhoneticString;
private Cursor mCursor;
private int mIdColumn;
@@ -353,8 +368,9 @@ public class VCardComposer {
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
- mUsesQPToPrimaryProperties = VCardConfig.usesQPToPrimaryProperties(vcardType);
+ mRefrainsQPToPrimaryProperties = VCardConfig.refrainsQPToPrimaryProperties(vcardType);
mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
+ mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType);
mHandlerList = new ArrayList<OneEntryHandler>();
if (mIsDoCoMo) {
@@ -569,7 +585,7 @@ public class VCardComposer {
appendOrganizations(builder, contentValuesListMap);
appendPhotos(builder, contentValuesListMap);
appendNotes(builder, contentValuesListMap);
- // TODO: GroupMembership
+ // TODO: GroupMembership, Relation, Event other than birthday.
if (mIsDoCoMo) {
appendVCardLine(builder, Constants.PROPERTY_X_CLASS, VCARD_DATA_PUBLIC);
@@ -650,21 +666,38 @@ public class VCardComposer {
// may get confused with multiple "N", "FN", etc. properties, though it is valid in
// vCard spec.
ContentValues primaryContentValues = null;
+ ContentValues subprimaryContentValues = null;
for (ContentValues contentValues : contentValuesList) {
+ if (contentValues == null){
+ continue;
+ }
Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY);
- if (isSuperPrimary != null && isSuperPrimary != 0) {
+ if (isSuperPrimary != null && isSuperPrimary > 0) {
// We choose "super primary" ContentValues.
primaryContentValues = contentValues;
break;
- } else if (primaryContentValues == null && contentValues != null) {
- // We choose the first ContentValues if "super primary" ContentValues does not exist.
- primaryContentValues = contentValues;
+ } else if (primaryContentValues == null) {
+ // We choose the first "primary" ContentValues
+ // if "super primary" ContentValues does not exist.
+ Integer primary = contentValues.getAsInteger(StructuredName.IS_PRIMARY);
+ if (primary != null && primary > 0) {
+ primaryContentValues = contentValues;
+ // Do not break, since there may be ContentValues with "super primary"
+ // afterword.
+ } else if (subprimaryContentValues == null) {
+ subprimaryContentValues = contentValues;
+ }
}
}
if (primaryContentValues == null) {
- Log.e(LOG_TAG, "All ContentValues given from database is empty.");
- primaryContentValues = new ContentValues();
+ if (subprimaryContentValues != null) {
+ // We choose the first ContentValues if any "primary" ContentValues does not exist.
+ primaryContentValues = subprimaryContentValues;
+ } else {
+ Log.e(LOG_TAG, "All ContentValues given from database is empty.");
+ primaryContentValues = new ContentValues();
+ }
}
final String familyName = primaryContentValues
@@ -688,7 +721,7 @@ public class VCardComposer {
final String encodedSuffix;
final boolean reallyUseQuotedPrintableToName =
- (mUsesQPToPrimaryProperties &&
+ (!mRefrainsQPToPrimaryProperties &&
!(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) &&
VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) &&
@@ -733,17 +766,17 @@ public class VCardComposer {
builder.append(encodedSuffix);
builder.append(VCARD_COL_SEPARATOR);
- final String fullname = VCardUtils.constructNameFromElements(
+ final String formattedName = VCardUtils.constructNameFromElements(
VCardConfig.getNameOrderType(mVCardType),
encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix);
final boolean reallyUseQuotedPrintableToFullname =
- mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(fullname);
+ !mRefrainsQPToPrimaryProperties &&
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedName);
final String encodedFullname =
reallyUseQuotedPrintableToFullname ?
- encodeQuotedPrintable(fullname) :
- escapeCharacters(fullname);
+ encodeQuotedPrintable(formattedName) :
+ escapeCharacters(formattedName);
// FN property
builder.append(Constants.PROPERTY_FN);
@@ -760,7 +793,7 @@ public class VCardComposer {
builder.append(VCARD_COL_SEPARATOR);
} else if (!TextUtils.isEmpty(displayName)) {
final boolean reallyUseQuotedPrintableToDisplayName =
- (mUsesQPToPrimaryProperties &&
+ (!mRefrainsQPToPrimaryProperties &&
!VCardUtils.containsOnlyNonCrLfPrintableAscii(displayName));
final String encodedDisplayName =
reallyUseQuotedPrintableToDisplayName ?
@@ -783,37 +816,53 @@ public class VCardComposer {
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_ITEM_SEPARATOR);
builder.append(VCARD_COL_SEPARATOR);
- } else if (mIsDoCoMo) {
- appendVCardLine(builder, Constants.PROPERTY_N, "");
+ if (mIsV30) {
+ builder.append(Constants.PROPERTY_FN);
+ // TODO: Not allowed formally...
+ if (shouldAppendCharsetAttribute(encodedDisplayName)) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedDisplayName);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
} else if (mIsV30) {
+ // vCard 3.0 specification requires these fields.
appendVCardLine(builder, Constants.PROPERTY_N, "");
appendVCardLine(builder, Constants.PROPERTY_FN, "");
+ } else if (mIsDoCoMo) {
+ appendVCardLine(builder, Constants.PROPERTY_N, "");
}
- String phoneticFamilyName = primaryContentValues
- .getAsString(StructuredName.PHONETIC_FAMILY_NAME);
- String phoneticMiddleName = primaryContentValues
- .getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
- String phoneticGivenName = primaryContentValues
- .getAsString(StructuredName.PHONETIC_GIVEN_NAME);
- if (!(TextUtils.isEmpty(phoneticFamilyName)
- && TextUtils.isEmpty(phoneticMiddleName) &&
- TextUtils.isEmpty(phoneticGivenName))) { // if not empty
- if (mIsJapaneseMobilePhone) {
- phoneticFamilyName = VCardUtils
- .toHalfWidthString(phoneticFamilyName);
- phoneticMiddleName = VCardUtils
- .toHalfWidthString(phoneticMiddleName);
- phoneticGivenName = VCardUtils
- .toHalfWidthString(phoneticGivenName);
+ final String phoneticFamilyName;
+ final String phoneticMiddleName;
+ final String phoneticGivenName;
+ {
+ String tmpPhoneticFamilyName =
+ primaryContentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
+ String tmpPhoneticMiddleName =
+ primaryContentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
+ String tmpPhoneticGivenName =
+ primaryContentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
+ if (mNeedsToConvertPhoneticString) {
+ phoneticFamilyName = VCardUtils.toHalfWidthString(tmpPhoneticFamilyName);
+ phoneticMiddleName = VCardUtils.toHalfWidthString(tmpPhoneticMiddleName);
+ phoneticGivenName = VCardUtils.toHalfWidthString(tmpPhoneticGivenName);
+ } else {
+ phoneticFamilyName = tmpPhoneticFamilyName;
+ phoneticMiddleName = tmpPhoneticMiddleName;
+ phoneticGivenName = tmpPhoneticGivenName;
}
+ }
+ if (!(TextUtils.isEmpty(phoneticFamilyName)
+ && TextUtils.isEmpty(phoneticMiddleName)
+ && TextUtils.isEmpty(phoneticGivenName))) {
if (mIsV30) {
final String sortString = VCardUtils
.constructNameFromElements(mVCardType,
- phoneticFamilyName,
- phoneticMiddleName,
- phoneticGivenName);
+ phoneticFamilyName, phoneticMiddleName, phoneticGivenName);
builder.append(Constants.PROPERTY_SORT_STRING);
// Do not need to care about QP, since vCard 3.0 does not allow it.
@@ -825,11 +874,12 @@ public class VCardComposer {
builder.append(VCARD_DATA_SEPARATOR);
builder.append(encodedSortString);
builder.append(VCARD_COL_SEPARATOR);
- } else {
+ } else if (mIsJapaneseMobilePhone) {
// 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
+ // We chose to use DoCoMo's way when the device is Japanese one
+ // 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(Constants.PROPERTY_SOUND);
@@ -837,13 +887,13 @@ public class VCardComposer {
builder.append(Constants.ATTR_TYPE_X_IRMC_N);
boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !(VCardUtils.containsOnlyNonCrLfPrintableAscii(
- phoneticFamilyName) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(
- phoneticMiddleName) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(
- phoneticGivenName)));
+ (!mRefrainsQPToPrimaryProperties
+ && !(VCardUtils.containsOnlyNonCrLfPrintableAscii(
+ phoneticFamilyName)
+ && VCardUtils.containsOnlyNonCrLfPrintableAscii(
+ phoneticMiddleName)
+ && VCardUtils.containsOnlyNonCrLfPrintableAscii(
+ phoneticGivenName)));
final String encodedPhoneticFamilyName;
final String encodedPhoneticMiddleName;
@@ -889,8 +939,7 @@ public class VCardComposer {
if (mUsesDefactProperty) {
if (!TextUtils.isEmpty(phoneticGivenName)) {
final boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName));
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName);
final String encodedPhoneticGivenName;
if (reallyUseQuotedPrintable) {
encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName);
@@ -912,8 +961,7 @@ public class VCardComposer {
}
if (!TextUtils.isEmpty(phoneticMiddleName)) {
final boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName));
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName);
final String encodedPhoneticMiddleName;
if (reallyUseQuotedPrintable) {
encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName);
@@ -935,8 +983,7 @@ public class VCardComposer {
}
if (!TextUtils.isEmpty(phoneticFamilyName)) {
final boolean reallyUseQuotedPrintable =
- (mUsesQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName));
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName);
final String encodedPhoneticFamilyName;
if (reallyUseQuotedPrintable) {
encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName);
@@ -963,46 +1010,48 @@ public class VCardComposer {
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Nickname.CONTENT_ITEM_TYPE);
- if (contentValuesList != null) {
- final String propertyNickname;
- if (mIsV30) {
- propertyNickname = Constants.PROPERTY_NICKNAME;
- /*} else if (mUsesAndroidProperty) {
- propertyNickname = VCARD_PROPERTY_X_NICKNAME;*/
- } else {
- // There's no way to add this field.
- return;
- }
+ if (contentValuesList == null) {
+ return;
+ }
- for (ContentValues contentValues : contentValuesList) {
- final String nickname = contentValues.getAsString(Nickname.NAME);
- if (TextUtils.isEmpty(nickname)) {
- continue;
- }
+ final String propertyNickname;
+ if (mIsV30) {
+ propertyNickname = Constants.PROPERTY_NICKNAME;
+ /*} else if (mUsesAndroidProperty) {
+ propertyNickname = VCARD_PROPERTY_X_NICKNAME;*/
+ } else {
+ // There's no way to add this field.
+ return;
+ }
- final String encodedNickname;
- final boolean reallyUseQuotedPrintable =
- (mUsesQuotedPrintable &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(nickname));
- if (reallyUseQuotedPrintable) {
- encodedNickname = encodeQuotedPrintable(nickname);
- } else {
- encodedNickname = escapeCharacters(nickname);
- }
+ for (ContentValues contentValues : contentValuesList) {
+ final String nickname = contentValues.getAsString(Nickname.NAME);
+ if (TextUtils.isEmpty(nickname)) {
+ continue;
+ }
- 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);
+ 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(encodedNickname);
+ builder.append(VCARD_COL_SEPARATOR);
}
}
@@ -1016,6 +1065,9 @@ public class VCardComposer {
for (ContentValues contentValues : contentValuesList) {
final Integer typeAsObject = contentValues.getAsInteger(Phone.TYPE);
final String label = contentValues.getAsString(Phone.LABEL);
+ final Integer isPrimaryAsInteger = contentValues.getAsInteger(Phone.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
String phoneNumber = contentValues.getAsString(Phone.NUMBER);
if (phoneNumber != null) {
phoneNumber = phoneNumber.trim();
@@ -1024,18 +1076,18 @@ public class VCardComposer {
continue;
}
phoneLineExists = true;
- int type = (typeAsObject != null ? typeAsObject : Phone.TYPE_HOME);
+ int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE);
// TODO: Premature, since this allows two phone numbers which are
// same from the view of phone number format (e.g. "100" v.s. "1-0-0")
if (!phoneSet.contains(phoneNumber)) {
phoneSet.add(phoneNumber);
- appendVCardTelephoneLine(builder, type, label, phoneNumber);
+ appendVCardTelephoneLine(builder, type, label, phoneNumber, isPrimary);
}
}
}
if (!phoneLineExists && mIsDoCoMo) {
- appendVCardTelephoneLine(builder, Phone.TYPE_HOME, "", "");
+ appendVCardTelephoneLine(builder, Phone.TYPE_HOME, "", "", false);
}
}
@@ -1043,14 +1095,11 @@ public class VCardComposer {
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Email.CONTENT_ITEM_TYPE);
+
boolean emailAddressExists = false;
if (contentValuesList != null) {
- Set<String> addressSet = new HashSet<String>();
+ final Set<String> addressSet = new HashSet<String>();
for (ContentValues contentValues : contentValuesList) {
- Integer typeAsObject = contentValues.getAsInteger(Email.TYPE);
- final int type = (typeAsObject != null ?
- typeAsObject : Email.TYPE_OTHER);
- final String label = contentValues.getAsString(Email.LABEL);
String emailAddress = contentValues.getAsString(Email.DATA);
if (emailAddress != null) {
emailAddress = emailAddress.trim();
@@ -1058,16 +1107,23 @@ public class VCardComposer {
if (TextUtils.isEmpty(emailAddress)) {
continue;
}
+ Integer typeAsObject = contentValues.getAsInteger(Email.TYPE);
+ final int type = (typeAsObject != null ?
+ typeAsObject : DEFAULT_EMAIL_TYPE);
+ final String label = contentValues.getAsString(Email.LABEL);
+ Integer isPrimaryAsInteger = contentValues.getAsInteger(Email.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
emailAddressExists = true;
if (!addressSet.contains(emailAddress)) {
addressSet.add(emailAddress);
- appendVCardEmailLine(builder, type, label, emailAddress);
+ appendVCardEmailLine(builder, type, label, emailAddress, isPrimary);
}
}
}
if (!emailAddressExists && mIsDoCoMo) {
- appendVCardEmailLine(builder, Email.TYPE_HOME, "", "");
+ appendVCardEmailLine(builder, Email.TYPE_HOME, "", "", false);
}
}
@@ -1124,7 +1180,10 @@ public class VCardComposer {
final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE);
final String label = contentValues.getAsString(StructuredPostal.LABEL);
if (type == preferedType) {
- appendVCardPostalLine(builder, type, label, contentValues);
+ // Note: Not sure why we need to emit "empty" line even when actual
+ // data does not exist. There may be some reason or may not.
+ // We keep safer side since the previous implementation did so.
+ appendVCardPostalLine(builder, type, label, contentValues, true, true);
return true;
}
}
@@ -1134,11 +1193,18 @@ public class VCardComposer {
private void appendPostalsForGeneric(final StringBuilder builder,
final List<ContentValues> contentValuesList) {
for (ContentValues contentValues : contentValuesList) {
- final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE);
- final String label = contentValues.getAsString(StructuredPostal.LABEL);
- if (type != null) {
- appendVCardPostalLine(builder, type, label, contentValues);
+ if (contentValues == null) {
+ continue;
}
+ final Integer typeAsObject = contentValues.getAsInteger(StructuredPostal.TYPE);
+ final int type = (typeAsObject != null ?
+ typeAsObject : DEFAULT_POSTAL_TYPE);
+ final String label = contentValues.getAsString(StructuredPostal.LABEL);
+ final Integer isPrimaryAsInteger =
+ contentValues.getAsInteger(StructuredPostal.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
+ appendVCardPostalLine(builder, type, label, contentValues, isPrimary, false);
}
}
@@ -1146,24 +1212,63 @@ public class VCardComposer {
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Im.CONTENT_ITEM_TYPE);
- if (contentValuesList != null) {
- for (ContentValues contentValues : contentValuesList) {
- Integer protocol = contentValues.getAsInteger(Im.PROTOCOL);
- String data = contentValues.getAsString(Im.DATA);
- if (data != null) {
- data = data.trim();
- }
- if (TextUtils.isEmpty(data)) {
- continue;
- }
-
- if (protocol != null && protocol == Im.PROTOCOL_GOOGLE_TALK) {
- if (VCardConfig.usesAndroidSpecificProperty(mVCardType)) {
- appendVCardLine(builder, Constants.PROPERTY_X_GOOGLE_TALK, data);
+ if (contentValuesList == null) {
+ return;
+ }
+ for (ContentValues contentValues : contentValuesList) {
+ final Integer protocolAsObject = contentValues.getAsInteger(Im.PROTOCOL);
+ if (protocolAsObject == null) {
+ continue;
+ }
+ final String propertyName = VCardUtils.getPropertyNameForIm(protocolAsObject);
+ if (propertyName == null) {
+ continue;
+ }
+ String data = contentValues.getAsString(Im.DATA);
+ if (data != null) {
+ data = data.trim();
+ }
+ if (TextUtils.isEmpty(data)) {
+ continue;
+ }
+ final String typeAsString;
+ {
+ final Integer typeAsInteger = contentValues.getAsInteger(Im.TYPE);
+ switch (typeAsInteger != null ? typeAsInteger : Im.TYPE_OTHER) {
+ case Im.TYPE_HOME: {
+ typeAsString = Constants.ATTR_TYPE_HOME;
+ break;
+ }
+ case Im.TYPE_WORK: {
+ typeAsString = Constants.ATTR_TYPE_WORK;
+ break;
+ }
+ case Im.TYPE_CUSTOM: {
+ final String label = contentValues.getAsString(Im.LABEL);
+ typeAsString = (label != null ? "X-" + label : null);
+ break;
+ }
+ case Im.TYPE_OTHER: // Ignore
+ default: {
+ typeAsString = null;
+ break;
}
- // TODO: add "X-GOOGLE TALK" case...
}
}
+
+ List<String> attributeList = new ArrayList<String>();
+ if (!TextUtils.isEmpty(typeAsString)) {
+ attributeList.add(typeAsString);
+ }
+ final Integer isPrimaryAsInteger = contentValues.getAsInteger(Im.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
+ }
+
+ appendVCardLineWithCharsetAndQPDetection(
+ builder, propertyName, attributeList, data);
}
}
@@ -1171,39 +1276,83 @@ public class VCardComposer {
final Map<String, List<ContentValues>> contentValuesListMap) {
final List<ContentValues> contentValuesList = contentValuesListMap
.get(Website.CONTENT_ITEM_TYPE);
- if (contentValuesList != null) {
- for (ContentValues contentValues : contentValuesList) {
- String website = contentValues.getAsString(Website.URL);
- if (website != null) {
- website = website.trim();
- }
- if (!TextUtils.isEmpty(website)) {
- appendVCardLine(builder, Constants.PROPERTY_URL, website);
- }
+ if (contentValuesList == null) {
+ return;
+ }
+ for (ContentValues contentValues : contentValuesList) {
+ String website = contentValues.getAsString(Website.URL);
+ if (website != null) {
+ website = website.trim();
+ }
+ // Note: vCard 3.0 does not allow any attribute addition toward "URL"
+ // property, while there's no document in vCard 2.1.
+ //
+ // TODO: Should we allow adding it when appropriate?
+ // (Actually, we drop some data. Using "group.X-URL-TYPE" or something
+ // may help)
+ if (!TextUtils.isEmpty(website)) {
+ appendVCardLine(builder, Constants.PROPERTY_URL, website);
}
}
}
+ /**
+ * Theoretically, there must be only one birthday for each vCard entry.
+ * Also, we are afraid of some importer's parse error during its import.
+ * We emit only one birthday entry even when there are more than one.
+ */
private void appendBirthday(final StringBuilder builder,
final Map<String, List<ContentValues>> contentValuesListMap) {
- final List<ContentValues> contentValuesList = contentValuesListMap
- .get(Event.CONTENT_ITEM_TYPE);
- if (contentValuesList != null && contentValuesList.size() > 0) {
- Integer eventType = contentValuesList.get(0).getAsInteger(Event.TYPE);
- if (eventType == null || !eventType.equals(Event.TYPE_BIRTHDAY)) {
- return;
+ final List<ContentValues> contentValuesList =
+ contentValuesListMap.get(Event.CONTENT_ITEM_TYPE);
+ if (contentValuesList == null) {
+ return;
+ }
+ String primaryBirthday = null;
+ String secondaryBirthday = null;
+ for (ContentValues contentValues : contentValuesList) {
+ if (contentValues == null) {
+ continue;
}
- // Theoretically, there must be only one birthday for each vCard data and
- // we are afraid of some parse error occuring in some devices, so
- // we emit only one birthday entry for now.
- String birthday = contentValuesList.get(0).getAsString(Event.START_DATE);
- if (birthday != null) {
- birthday = birthday.trim();
+ final Integer eventType = contentValues.getAsInteger(Event.TYPE);
+ if (eventType == null || !eventType.equals(Event.TYPE_BIRTHDAY)) {
+ continue;
+ }
+ final String birthdayCandidate = contentValues.getAsString(Event.START_DATE);
+ if (birthdayCandidate == null) {
+ continue;
+ }
+ final Integer isSuperPrimaryAsInteger =
+ contentValues.getAsInteger(Event.IS_SUPER_PRIMARY);
+ final boolean isSuperPrimary = (isSuperPrimaryAsInteger != null ?
+ (isSuperPrimaryAsInteger > 0) : false);
+ if (isSuperPrimary) {
+ // "super primary" birthday should the prefered one.
+ primaryBirthday = birthdayCandidate;
+ break;
}
- if (!TextUtils.isEmpty(birthday)) {
- appendVCardLine(builder, Constants.PROPERTY_BDAY, birthday);
+ final Integer isPrimaryAsInteger =
+ contentValues.getAsInteger(Event.IS_PRIMARY);
+ final boolean isPrimary = (isPrimaryAsInteger != null ?
+ (isPrimaryAsInteger > 0) : false);
+ if (isPrimary) {
+ // We don't break here since "super primary" birthday may exist later.
+ primaryBirthday = birthdayCandidate;
+ } else if (secondaryBirthday == null) {
+ // First entry is set to the "secondary" candidate.
+ secondaryBirthday = birthdayCandidate;
}
}
+
+ final String birthday;
+ if (primaryBirthday != null) {
+ birthday = primaryBirthday.trim();
+ } else if (secondaryBirthday != null){
+ birthday = secondaryBirthday.trim();
+ } else {
+ return;
+ }
+ appendVCardLineWithCharsetAndQPDetection(builder, Constants.PROPERTY_BDAY, birthday);
}
private void appendOrganizations(final StringBuilder builder,
@@ -1212,23 +1361,35 @@ public class VCardComposer {
.get(Organization.CONTENT_ITEM_TYPE);
if (contentValuesList != null) {
for (ContentValues contentValues : contentValuesList) {
- String company = contentValues
- .getAsString(Organization.COMPANY);
+ String company = contentValues.getAsString(Organization.COMPANY);
if (company != null) {
company = company.trim();
}
- String title = contentValues
- .getAsString(Organization.TITLE);
+ String department = contentValues.getAsString(Organization.DEPARTMENT);
+ if (department != null) {
+ department = department.trim();
+ }
+ String title = contentValues.getAsString(Organization.TITLE);
if (title != null) {
title = title.trim();
}
+ StringBuilder orgBuilder = new StringBuilder();
if (!TextUtils.isEmpty(company)) {
- appendVCardLine(builder, Constants.PROPERTY_ORG, company,
- !VCardUtils.containsOnlyPrintableAscii(company),
- (mUsesQuotedPrintable &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(company)));
+ orgBuilder.append(company);
+ }
+ if (!TextUtils.isEmpty(department)) {
+ if (orgBuilder.length() > 0) {
+ orgBuilder.append(';');
+ }
+ orgBuilder.append(department);
}
+ final String orgline = orgBuilder.toString();
+ appendVCardLine(builder, Constants.PROPERTY_ORG, orgline,
+ !VCardUtils.containsOnlyPrintableAscii(orgline),
+ (mUsesQuotedPrintable &&
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(orgline)));
+
if (!TextUtils.isEmpty(title)) {
appendVCardLine(builder, Constants.PROPERTY_TITLE, title,
!VCardUtils.containsOnlyPrintableAscii(title),
@@ -1257,11 +1418,9 @@ public class VCardComposer {
photoType = "GIF";
} else if (data.length >= 4 && data[0] == (byte) 0x89
&& data[1] == 'P' && data[2] == 'N' && data[3] == 'G') {
- // Note: vCard 2.1 officially does not support PNG, but we
- // may have it
- // and using X- word like "X-PNG" may not let importers know
- // it is
- // PNG. So we use the String "PNG" as is...
+ // Note: vCard 2.1 officially does not support PNG, but we may
+ // have it and using X- word like "X-PNG" may not let importers
+ // know it is PNG. So we use the String "PNG" as is...
photoType = "PNG";
} else if (data.length >= 2 && data[0] == (byte) 0xff
&& data[1] == (byte) 0xd8) {
@@ -1435,59 +1594,126 @@ public class VCardComposer {
builder.append(VCARD_COL_SEPARATOR);
}
- private void appendVCardPostalLine(final StringBuilder builder,
- final Integer typeAsObject, final String label,
- final ContentValues contentValues) {
- builder.append(Constants.PROPERTY_ADR);
- builder.append(VCARD_ATTR_SEPARATOR);
+ private class PostalStruct {
+ final boolean reallyUseQuotedPrintable;
+ final boolean appendCharset;
+ final String addressData;
+ public PostalStruct(final boolean reallyUseQuotedPrintable,
+ final boolean appendCharset, final String addressData) {
+ this.reallyUseQuotedPrintable = reallyUseQuotedPrintable;
+ this.appendCharset = appendCharset;
+ this.addressData = addressData;
+ }
+ }
- // Note: Not sure why we need to emit "empty" line even when actual data does not exist.
- // There may be some reason or may not be any. We keep safer side.
- // TODO: investigate this.
- boolean dataExists = false;
+ /**
+ * @return null when there's no information available to construct the data.
+ */
+ private PostalStruct tryConstructPostalStruct(ContentValues contentValues) {
+ boolean reallyUseQuotedPrintable = false;
+ boolean appendCharset = false;
+
+ boolean dataArrayExists = false;
String[] dataArray = VCardUtils.getVCardPostalElements(contentValues);
- boolean actuallyUseQuotedPrintable = false;
- boolean shouldAppendCharset = false;
for (String data : dataArray) {
if (!TextUtils.isEmpty(data)) {
- dataExists = true;
- if (!shouldAppendCharset && !VCardUtils.containsOnlyPrintableAscii(data)) {
- shouldAppendCharset = true;
+ dataArrayExists = true;
+ if (!appendCharset && !VCardUtils.containsOnlyPrintableAscii(data)) {
+ appendCharset = true;
}
if (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(data)) {
- actuallyUseQuotedPrintable = true;
+ reallyUseQuotedPrintable = true;
break;
}
}
}
- int length = dataArray.length;
- for (int i = 0; i < length; i++) {
- String data = dataArray[i];
- if (!TextUtils.isEmpty(data)) {
- if (actuallyUseQuotedPrintable) {
- dataArray[i] = encodeQuotedPrintable(data);
+ if (dataArrayExists) {
+ StringBuffer addressBuffer = new StringBuffer();
+ boolean first = true;
+ for (String data : dataArray) {
+ if (first) {
+ first = false;
} else {
- dataArray[i] = escapeCharacters(data);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ }
+ if (!TextUtils.isEmpty(data)) {
+ if (reallyUseQuotedPrintable) {
+ addressBuffer.append(encodeQuotedPrintable(data));
+ } else {
+ addressBuffer.append(escapeCharacters(data));
+ }
}
}
+ return new PostalStruct(reallyUseQuotedPrintable, appendCharset,
+ addressBuffer.toString());
}
- final int typeAsPrimitive;
- if (typeAsObject == null) {
- typeAsPrimitive = StructuredPostal.TYPE_OTHER;
- } else {
- typeAsPrimitive = typeAsObject;
+ String formattedAddress =
+ contentValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
+ if (!TextUtils.isEmpty(formattedAddress)) {
+ reallyUseQuotedPrintable =
+ !VCardUtils.containsOnlyPrintableAscii(formattedAddress);
+ appendCharset =
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedAddress);
+ if (reallyUseQuotedPrintable) {
+ formattedAddress = encodeQuotedPrintable(formattedAddress);
+ } else {
+ formattedAddress = escapeCharacters(formattedAddress);
+ }
+ // We use the second value ("Extended Address").
+ //
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal
+ // ; Code, Country Name
+ StringBuffer addressBuffer = new StringBuffer();
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(formattedAddress);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ addressBuffer.append(VCARD_ITEM_SEPARATOR);
+ return new PostalStruct(
+ reallyUseQuotedPrintable, appendCharset, addressBuffer.toString());
+ }
+ return null; // There's no data available.
+ }
+
+ private void appendVCardPostalLine(final StringBuilder builder,
+ final int type, final String label, final ContentValues contentValues,
+ final boolean isPrimary, final boolean emitLineEveryTime) {
+ final boolean reallyUseQuotedPrintable;
+ final boolean appendCharset;
+ final String addressData;
+ {
+ PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
+ if (postalStruct == null) {
+ if (emitLineEveryTime) {
+ reallyUseQuotedPrintable = false;
+ appendCharset = false;
+ addressData = "";
+ } else {
+ return;
+ }
+ } else {
+ reallyUseQuotedPrintable = postalStruct.reallyUseQuotedPrintable;
+ appendCharset = postalStruct.appendCharset;
+ addressData = postalStruct.addressData;
+ }
}
- String typeAsString = null;
- switch (typeAsPrimitive) {
+ List<String> attributeList = new ArrayList<String>();
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
+ }
+ switch (type) {
case StructuredPostal.TYPE_HOME: {
- typeAsString = Constants.ATTR_TYPE_HOME;
+ attributeList.add(Constants.ATTR_TYPE_HOME);
break;
}
case StructuredPostal.TYPE_WORK: {
- typeAsString = Constants.ATTR_TYPE_WORK;
+ attributeList.add(Constants.ATTR_TYPE_WORK);
break;
}
case StructuredPostal.TYPE_CUSTOM: {
@@ -1497,9 +1723,7 @@ public class VCardComposer {
// ("IANA-token" in the vCard 3.0 is unclear...)
// Just for safety, we add "X-" at the beggining of each label.
// Also checks the label obeys with vCard 3.0 spec.
- builder.append("X-");
- builder.append(label);
- builder.append(VCARD_DATA_SEPARATOR);
+ attributeList.add("X-" + label);
}
break;
}
@@ -1507,82 +1731,56 @@ public class VCardComposer {
break;
}
default: {
- Log.e(LOG_TAG, "Unknown StructuredPostal type: " + typeAsPrimitive);
+ Log.e(LOG_TAG, "Unknown StructuredPostal type: " + type);
break;
}
}
- // Attribute(s).
+ // Actual data construction starts from here.
+ // TODO: add a new version of appendVCardLine() for this purpose.
+ builder.append(Constants.PROPERTY_ADR);
+ builder.append(VCARD_ATTR_SEPARATOR);
+
+ // Attributes
{
boolean shouldAppendAttrSeparator = false;
- if (typeAsString != null) {
- appendTypeAttribute(builder, typeAsString);
+ if (!attributeList.isEmpty()) {
+ appendTypeAttributes(builder, attributeList);
shouldAppendAttrSeparator = true;
}
- if (dataExists) {
- if (shouldAppendCharset) {
- // Strictly, vCard 3.0 does not allow exporters to emit charset information,
- // but we will add it since the information should be useful for importers,
- //
- // Assume no parser does not emit error with this attribute in vCard 3.0.
- if (shouldAppendAttrSeparator) {
- builder.append(VCARD_ATTR_SEPARATOR);
- }
- builder.append(mVCardAttributeCharset);
- shouldAppendAttrSeparator = true;
+ if (appendCharset) {
+ // Strictly, vCard 3.0 does not allow exporters to emit charset information,
+ // but we will add it since the information should be useful for importers,
+ //
+ // Assume no parser does not emit error with this attribute in vCard 3.0.
+ if (shouldAppendAttrSeparator) {
+ builder.append(VCARD_ATTR_SEPARATOR);
}
+ builder.append(mVCardAttributeCharset);
+ shouldAppendAttrSeparator = true;
+ }
- if (actuallyUseQuotedPrintable) {
- if (shouldAppendAttrSeparator) {
- builder.append(VCARD_ATTR_SEPARATOR);
- }
- builder.append(VCARD_ATTR_ENCODING_QP);
- shouldAppendAttrSeparator = true;
+ if (reallyUseQuotedPrintable) {
+ if (shouldAppendAttrSeparator) {
+ builder.append(VCARD_ATTR_SEPARATOR);
}
+ builder.append(VCARD_ATTR_ENCODING_QP);
+ shouldAppendAttrSeparator = true;
}
}
- // Property values.
-
builder.append(VCARD_DATA_SEPARATOR);
- if (dataExists) {
- // The elements in dataArray are already encoded to quoted printable
- // if needed.
- // See above.
- //
- // TODO: in vCard 3.0, one line may become too huge. Fix this.
- builder.append(dataArray[0]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[1]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[2]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[3]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[4]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[5]);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(dataArray[6]);
- }
+ builder.append(addressData);
builder.append(VCARD_COL_SEPARATOR);
}
private void appendVCardEmailLine(final StringBuilder builder,
- final Integer typeAsObject, final String label, final String data) {
- builder.append(Constants.PROPERTY_EMAIL);
-
- final int typeAsPrimitive;
- if (typeAsObject == null) {
- typeAsPrimitive = Email.TYPE_OTHER;
- } else {
- typeAsPrimitive = typeAsObject;
- }
-
+ final int type, final String label,
+ final String rawData, final boolean isPrimary) {
final String typeAsString;
- switch (typeAsPrimitive) {
+ switch (type) {
case Email.TYPE_CUSTOM: {
// For backward compatibility.
// Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
@@ -1594,7 +1792,7 @@ public class VCardComposer {
&& VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
typeAsString = "X-" + label;
} else {
- typeAsString = DEFAULT_EMAIL_TYPE;
+ typeAsString = null;
}
break;
}
@@ -1607,7 +1805,7 @@ public class VCardComposer {
break;
}
case Email.TYPE_OTHER: {
- typeAsString = DEFAULT_EMAIL_TYPE;
+ typeAsString = null;
break;
}
case Email.TYPE_MOBILE: {
@@ -1615,22 +1813,27 @@ public class VCardComposer {
break;
}
default: {
- Log.e(LOG_TAG, "Unknown Email type: " + typeAsPrimitive);
- typeAsString = DEFAULT_EMAIL_TYPE;
+ Log.e(LOG_TAG, "Unknown Email type: " + type);
+ typeAsString = null;
break;
}
}
- builder.append(VCARD_ATTR_SEPARATOR);
- appendTypeAttribute(builder, typeAsString);
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(data);
- builder.append(VCARD_COL_SEPARATOR);
+ final List<String> attributeList = new ArrayList<String>();
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
+ }
+ if (!TextUtils.isEmpty(typeAsString)) {
+ attributeList.add(typeAsString);
+ }
+
+ appendVCardLineWithCharsetAndQPDetection(builder, Constants.PROPERTY_EMAIL,
+ attributeList, rawData);
}
private void appendVCardTelephoneLine(final StringBuilder builder,
final Integer typeAsObject, final String label,
- String encodedData) {
+ final String encodedData, boolean isPrimary) {
builder.append(Constants.PROPERTY_TEL);
builder.append(VCARD_ATTR_SEPARATOR);
@@ -1641,53 +1844,102 @@ public class VCardComposer {
typeAsPrimitive = typeAsObject;
}
+ ArrayList<String> attributeList = new ArrayList<String>();
switch (typeAsPrimitive) {
case Phone.TYPE_HOME:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_VOICE));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_VOICE));
break;
case Phone.TYPE_WORK:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_VOICE));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_VOICE));
break;
case Phone.TYPE_FAX_HOME:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_FAX));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_FAX));
break;
case Phone.TYPE_FAX_WORK:
- appendTypeAttributes(builder, Arrays.asList(
- Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_FAX));
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_FAX));
break;
case Phone.TYPE_MOBILE:
- builder.append(Constants.ATTR_TYPE_CELL);
+ attributeList.add(Constants.ATTR_TYPE_CELL);
break;
case Phone.TYPE_PAGER:
if (mIsDoCoMo) {
// Not sure about the reason, but previous implementation had
// used "VOICE" instead of "PAGER"
- // Also, refrain from using appendType() so that "TYPE=" is never be appended.
- builder.append(Constants.ATTR_TYPE_VOICE);
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
} else {
- appendTypeAttribute(builder, Constants.ATTR_TYPE_PAGER);
+ attributeList.add(Constants.ATTR_TYPE_PAGER);
}
break;
case Phone.TYPE_OTHER:
- appendTypeAttribute(builder, Constants.ATTR_TYPE_VOICE);
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
+ break;
+ case Phone.TYPE_CAR:
+ attributeList.add(Constants.ATTR_TYPE_CAR);
+ break;
+ case Phone.TYPE_COMPANY_MAIN:
+ // There's no relevant field in vCard (at least 2.1).
+ attributeList.add(Constants.ATTR_TYPE_WORK);
+ isPrimary = true;
+ break;
+ case Phone.TYPE_ISDN:
+ attributeList.add(Constants.ATTR_TYPE_ISDN);
+ break;
+ case Phone.TYPE_MAIN:
+ isPrimary = true;
+ break;
+ case Phone.TYPE_OTHER_FAX:
+ attributeList.add(Constants.ATTR_TYPE_FAX);
+ break;
+ case Phone.TYPE_TELEX:
+ attributeList.add(Constants.ATTR_TYPE_TLX);
+ break;
+ case Phone.TYPE_WORK_MOBILE:
+ attributeList.addAll(
+ Arrays.asList(Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_CELL));
+ break;
+ case Phone.TYPE_WORK_PAGER:
+ attributeList.add(Constants.ATTR_TYPE_WORK);
+ // See above.
+ if (mIsDoCoMo) {
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
+ } else {
+ attributeList.add(Constants.ATTR_TYPE_PAGER);
+ }
+ break;
+ case Phone.TYPE_MMS:
+ attributeList.add(Constants.ATTR_TYPE_MSG);
break;
case Phone.TYPE_CUSTOM:
if (mUsesAndroidProperty && !TextUtils.isEmpty(label)
&& VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
- appendTypeAttribute(builder, "X-" + label);
+ // Note: Strictly, vCard 2.1 does not allow "X-" attribute without
+ // "TYPE=" string.
+ attributeList.add("X-" + label);
} else {
// Just ignore the custom type.
- appendTypeAttribute(builder, Constants.ATTR_TYPE_VOICE);
+ attributeList.add(Constants.ATTR_TYPE_VOICE);
}
break;
+ case Phone.TYPE_RADIO:
+ case Phone.TYPE_TTY_TDD:
default:
- appendUncommonPhoneType(builder, typeAsPrimitive);
break;
}
+ if (isPrimary) {
+ attributeList.add(Constants.ATTR_TYPE_PREF);
+ }
+
+ if (attributeList.isEmpty()) {
+ appendUncommonPhoneType(builder, typeAsPrimitive);
+ } else {
+ appendTypeAttributes(builder, attributeList);
+ }
+
builder.append(VCARD_DATA_SEPARATOR);
builder.append(encodedData);
builder.append(VCARD_COL_SEPARATOR);
@@ -1711,15 +1963,43 @@ public class VCardComposer {
}
}
+ private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
+ final String propertyName, final String rawData) {
+ appendVCardLineWithCharsetAndQPDetection(builder, propertyName, null, rawData);
+ }
+
+ private void appendVCardLineWithCharsetAndQPDetection(final StringBuilder builder,
+ final String propertyName,
+ final List<String> attributeList, final String rawData) {
+ final boolean needCharset =
+ (mUsesQuotedPrintable && !VCardUtils.containsOnlyPrintableAscii(rawData));
+ final boolean reallyUseQuotedPrintable =
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawData);
+ appendVCardLine(builder, propertyName, attributeList,
+ rawData, needCharset, reallyUseQuotedPrintable);
+ }
+
private void appendVCardLine(final StringBuilder builder,
final String propertyName, final String rawData) {
appendVCardLine(builder, propertyName, rawData, false, false);
}
private void appendVCardLine(final StringBuilder builder,
- final String field, final String rawData, final boolean needCharset,
+ final String propertyName, final String rawData, final boolean needCharset,
+ boolean needQuotedPrintable) {
+ appendVCardLine(builder, propertyName, null, rawData, needCharset, needQuotedPrintable);
+ }
+
+ private void appendVCardLine(final StringBuilder builder,
+ final String propertyName,
+ final List<String> attributeList,
+ final String rawData, final boolean needCharset,
boolean needQuotedPrintable) {
- builder.append(field);
+ 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);
@@ -1741,6 +2021,9 @@ public class VCardComposer {
builder.append(VCARD_COL_SEPARATOR);
}
+ /**
+ * VCARD_ATTR_SEPARATOR must be appended before this method being called.
+ */
private void appendTypeAttributes(final StringBuilder builder,
final List<String> types) {
// We may have to make this comma separated form like "TYPE=DOM,WORK" in the future,
@@ -1756,9 +2039,15 @@ public class VCardComposer {
}
}
+ /**
+ * VCARD_ATTR_SEPARATOR must be appended before this method being called.
+ */
private void appendTypeAttribute(final StringBuilder builder, final String type) {
+ // Refrain from using appendType() so that "TYPE=" is not be appended when the
+ // device is DoCoMo's (just for safety).
+ //
// Note: In vCard 3.0, Type strings also can be like this: "TYPE=HOME,PREF"
- if (mIsV30 || mAppendTypeParamName) {
+ if ((mIsV30 || mAppendTypeParamName) && !mIsDoCoMo) {
builder.append(Constants.ATTR_TYPE).append(VCARD_ATTR_EQUAL);
}
builder.append(type);
@@ -1869,12 +2158,11 @@ public class VCardComposer {
if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) {
needCharset = true;
}
- // TODO: QP should be used? Using mUsesQPToPrimaryProperties should help.
appendVCardLine(builder, Constants.PROPERTY_FN, phoneName, needCharset, false);
appendVCardLine(builder, Constants.PROPERTY_N, phoneName, needCharset, false);
String label = Integer.toString(phonetype);
- appendVCardTelephoneLine(builder, phonetype, label, phoneNumber);
+ appendVCardTelephoneLine(builder, phonetype, label, phoneNumber, false);
appendVCardLine(builder, Constants.PROPERTY_END, VCARD_DATA_VCARD);
@@ -1948,7 +2236,6 @@ 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, Constants.PROPERTY_FN, name, needCharset, false);
appendVCardLine(builder, Constants.PROPERTY_N, name, needCharset, false);
@@ -1958,7 +2245,7 @@ public class VCardComposer {
if (TextUtils.isEmpty(label)) {
label = Integer.toString(type);
}
- appendVCardTelephoneLine(builder, type, label, number);
+ appendVCardTelephoneLine(builder, type, label, number, false);
tryAppendCallHistoryTimeStampField(builder);
appendVCardLine(builder, Constants.PROPERTY_END, VCARD_DATA_VCARD);
return builder.toString();
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
index 03ed329..9581c74 100644
--- a/core/java/android/pim/vcard/VCardConfig.java
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -15,14 +15,20 @@
*/
package android.pim.vcard;
+import android.util.Log;
+
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
/**
* The class representing VCard related configurations. Useful static methods are not in this class
* but in VCardUtils.
*/
public class VCardConfig {
+ private static final String LOG_TAG = "vcard.VCardConfig";
+
// TODO: may be better to make the instance of this available and stop using static methods and
// one integer.
@@ -95,18 +101,63 @@ 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.
+ * <P>
+ * The flag indicating the vCard composer does "NOT" use Quoted-Printable toward "primary"
+ * properties even though it is required by vCard 2.1 (QP is prohibited in vCard 3.0).
+ * </P>
+ * <P>
+ * We actually cannot define what is the "primary" property. Note that this is NOT defined
+ * in vCard specification either. Also be aware that it is NOT related to "primary" notion
+ * used in {@link android.provider.ContactsContract}.
+ * This notion is just for vCard composition in Android.
+ * </P>
+ * <P>
+ * We added this Android-specific notion since some (incomplete) vCard exporters for vCard 2.1
+ * do NOT use Quoted-Printable encoding toward some properties like "N", "FN", etc. even when
+ * their values contain non-ascii or/and CR/LF, while they use the encoding in the other
+ * properties like "ADR", "ORG", etc.
+ * <P>
+ * We are afraid of the case where some vCard importer also forget handling QP presuming QP is
+ * not used in such fields.
+ * </P>
+ * <P>
+ * This flag is useful when some target importer you are going to focus on does not accept
+ * such "primary" property values with Quoted-Printable encoding.
+ * </P>
+ * <P>
+ * Again, we should not use this flag at all for complying vCard 2.1 spec.
+ * </P>
+ * <P>
+ * We will change the behavior around this flag in the future, after understanding the other
+ * real vCard cases around this problem. Please use this flag with extreme caution even when
+ * needed.
+ * </P>
+ * <P>
+ * In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
+ * kind of problem (hopefully).
+ * </P>
*/
- public static final int FLAG_USE_QP_TO_PRIMARY_PROPERTIES = 0x10000000;
+ public static final int FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES = 0x10000000;
/**
+ * <P>
+ * The flag indicating that phonetic name related fields must be converted to
+ * appropriate form. Note that "appropriate" is not defined in any vCard specification.
+ * This is Android-specific.
+ * </P>
+ * <P>
+ * One typical (and currently sole) example where we need this flag is the time when
+ * we need to emit Japanese phonetic names into vCard entries. The property values
+ * should be encoded into half-width katakana when the target importer is Japanese mobile
+ * phones', which are probably not able to parse full-width hiragana/katakana for
+ * historical reasons, while the vCard importers embedded to softwares for PC should be
+ * able to parse them as we expect.
+ * </P>
+ */
+ public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x0800000;
+
+ /**
+ * <P>
* The flag indicating the vCard composer "for 2.1" emits "TYPE=" string every time
* possible. The default behavior does not emit it and is valid, while adding "TYPE="
* is also valid. In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in
@@ -119,87 +170,118 @@ public class VCardConfig {
*
* e.g. int vcardType = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
*/
- public static final int FLAG_APPEND_TYPE_PARAM = 0x08000000;
+ public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
//// The followings are VCard types available from importer/exporter. ////
/**
+ * <P>
* 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.
- *
+ * When composing a vCard entry, the US convension will be used toward formatting
+ * some values
+ * </P>
+ * <P>
* e.g. The order of the display name would be "Prefix Given Middle Family Suffix",
- * while in Japan, it should be "Prefix Family Middle Given Suffix".
+ * while it should be "Prefix Family Middle Given Suffix" in Japan.
+ * </P>
*/
- public static final int VCARD_TYPE_V21_GENERIC =
+ public static final int VCARD_TYPE_V21_GENERIC_UTF8 =
(FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
+ /* package */ static String VCARD_TYPE_V21_GENERIC_UTF8_STR = "v21_generic";
/**
+ * <P>
* 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 both in
- * parsing and composing.
- *
- * TODO: implement this type correctly.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
- public static final int VCARD_TYPE_V30_GENERIC =
+ public static final int VCARD_TYPE_V30_GENERIC_UTF8 =
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic";
+ /* package */ static final String VCARD_TYPE_V30_GENERIC_UTF8_STR = "v30_generic";
/**
- * General vCard format with the version 2.1 with some Europe convension. Uses Utf-8.
+ * <P>
+ * General vCard format for the vCard 2.1 with some Europe convension. Uses Utf-8.
* Currently, only name order is considered ("Prefix Middle Given Family Suffix")
+ * </P>
*/
- public static final int VCARD_TYPE_V21_EUROPE =
+ public static final int VCARD_TYPE_V21_EUROPE_UTF8 =
(FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
+ /* package */ static final String VCARD_TYPE_V21_EUROPE_UTF8_STR = "v21_europe";
/**
+ * <P>
* General vCard format with the version 3.0 with some Europe convension. Uses UTF-8
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
- public static final int VCARD_TYPE_V30_EUROPE =
+ public static final int VCARD_TYPE_V30_EUROPE_UTF8 =
(FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
-
- /**
- * vCard 2.1 format for miscellaneous Japanese devices. Shift_Jis is used for
- * parsing/composing the vCard data.
- */
- public static final int VCARD_TYPE_V21_JAPANESE =
- (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
- FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese";
-
/**
- * vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * <P>
+ * The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
public static final int VCARD_TYPE_V21_JAPANESE_UTF8 =
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8";
+
+ /**
+ * <P>
+ * vCard 2.1 format for miscellaneous Japanese devices. Shift_Jis is used for
+ * parsing/composing the vCard data.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
+ */
+ public static final int VCARD_TYPE_V21_JAPANESE_SJIS =
+ (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V21_JAPANESE_SJIS_STR = "v21_japanese_sjis";
/**
+ * <P>
* vCard format for miscellaneous Japanese devices, using Shift_Jis for
* parsing/composing the vCard data.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
- public static final int VCARD_TYPE_V30_JAPANESE =
+ public static final int VCARD_TYPE_V30_JAPANESE_SJIS =
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
- /* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese";
+ /* package */ static final String VCARD_TYPE_V30_JAPANESE_SJIS_STR = "v30_japanese_sjis";
/**
- * vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * <P>
+ * The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
*/
public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
@@ -208,38 +290,72 @@ public class VCardConfig {
/* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8";
/**
- * VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
- * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
- * No Android-specific property nor defact property is included.
+ * <P>
+ * The vCard 2.1 based format which (partially) considers the convention in Japanese
+ * mobile phones, where phonetic names are translated to half-width katakana if
+ * possible, etc.
+ * </P>
+ * <P>
+ * Not ready yet. Use with caution when you use this.
+ * </P>
+ */
+ public static final int VCARD_TYPE_V21_JAPANESE_MOBILE =
+ (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
+ FLAG_CONVERT_PHONETIC_NAME_STRINGS |
+ FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES);
+
+ public static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile";
+
+ /**
+ * <P>
+ * VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
+ * </p>
+ * <P>
+ * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
+ * No Android-specific property nor defact property is included. The "Primary" properties
+ * are NOT encoded to Quoted-Printable.
+ * </P>
*/
public static final int VCARD_TYPE_DOCOMO =
- (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS | FLAG_DOCOMO);
+ (VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
private static final String VCARD_TYPE_DOCOMO_STR = "docomo";
- public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC;
+ public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC_UTF8;
- private static final Map<String, Integer> VCARD_TYPES_MAP;
+ private static final Map<String, Integer> sVCardTypeMap;
+ private static final Set<Integer> sJapaneseMobileTypeSet;
static {
- VCARD_TYPES_MAP = new HashMap<String, Integer>();
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
- VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
- VCARD_TYPES_MAP.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
+ sVCardTypeMap = new HashMap<String, Integer>();
+ sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_UTF8_STR, VCARD_TYPE_V21_GENERIC_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_UTF8_STR, VCARD_TYPE_V30_GENERIC_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_UTF8_STR, VCARD_TYPE_V21_EUROPE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_SJIS_STR, VCARD_TYPE_V21_JAPANESE_SJIS);
+ sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_SJIS_STR, VCARD_TYPE_V30_JAPANESE_SJIS);
+ sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
+ sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE);
+ sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
+
+ sJapaneseMobileTypeSet = new HashSet<Integer>();
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_SJIS);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_UTF8);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_SJIS);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_SJIS);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_UTF8);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
+ sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
}
public static int getVCardTypeFromString(String vcardTypeString) {
String loweredKey = vcardTypeString.toLowerCase();
- if (VCARD_TYPES_MAP.containsKey(loweredKey)) {
- return VCARD_TYPES_MAP.get(loweredKey);
+ if (sVCardTypeMap.containsKey(loweredKey)) {
+ return sVCardTypeMap.get(loweredKey);
} else {
// XXX: should return the value indicating the input is invalid?
+ Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\"");
return VCARD_TYPE_DEFAULT;
}
}
@@ -252,22 +368,6 @@ public class VCardConfig {
return !isV30(vcardType);
}
- public static boolean isDoCoMo(int vcardType) {
- return ((vcardType & FLAG_DOCOMO) != 0);
- }
-
- /**
- * @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) {
- return ((vcardType == VCARD_TYPE_V21_JAPANESE) ||
- (vcardType == VCARD_TYPE_V21_JAPANESE_UTF8) ||
- (vcardType == VCARD_TYPE_V30_JAPANESE) ||
- (vcardType == VCARD_TYPE_V30_JAPANESE_UTF8) ||
- (vcardType == VCARD_TYPE_DOCOMO));
- }
-
public static boolean usesUtf8(int vcardType) {
return ((vcardType & FLAG_CHARSET_UTF8) != 0);
}
@@ -276,17 +376,6 @@ public class VCardConfig {
return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0);
}
- /**
- * @return true when Japanese phonetic string must be converted to a string
- * containing only half-width katakana. This method exists since Japanese mobile
- * phones usually use only half-width katakana for expressing phonetic names and
- * some devices are not ready for parsing other phonetic strings like hiragana and
- * full-width katakana.
- */
- public static boolean needsToConvertPhoneticString(int vcardType) {
- return (vcardType == VCARD_TYPE_DOCOMO);
- }
-
public static int getNameOrderType(int vcardType) {
return vcardType & NAME_ORDER_MASK;
}
@@ -299,24 +388,37 @@ public class VCardConfig {
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;
}
+ public static boolean refrainsQPToPrimaryProperties(int vcardType) {
+ return (!usesQuotedPrintable(vcardType) ||
+ ((vcardType & FLAG_REFRAIN_QP_TO_PRIMARY_PROPERTIES) != 0));
+ }
+
+ public static boolean appendTypeParamName(int vcardType) {
+ return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0));
+ }
+
/**
- * @hide
+ * @return true if the device is Japanese and some Japanese convension is
+ * applied to creating "formatted" something like FORMATTED_ADDRESS.
*/
- public static boolean usesQPToPrimaryProperties(int vcardType) {
- return (usesQuotedPrintable(vcardType) &&
- ((vcardType & FLAG_USE_QP_TO_PRIMARY_PROPERTIES) != 0));
+ public static boolean isJapaneseDevice(int vcardType) {
+ return sJapaneseMobileTypeSet.contains(vcardType);
}
- public static boolean appendTypeParamName(int vcardType) {
- return (vcardType & FLAG_APPEND_TYPE_PARAM) != 0;
+ public static boolean needsToConvertPhoneticString(int vcardType) {
+ return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0);
+ }
+
+ public static boolean onlyOneNoteFieldIsAvailable(int vcardType) {
+ return vcardType == VCARD_TYPE_DOCOMO;
+ }
+
+ public static boolean isDoCoMo(int vcardType) {
+ return ((vcardType & FLAG_DOCOMO) != 0);
}
private VCardConfig() {
diff --git a/core/java/android/pim/vcard/VCardDataBuilder.java b/core/java/android/pim/vcard/VCardDataBuilder.java
index d00f616..76ad482 100644
--- a/core/java/android/pim/vcard/VCardDataBuilder.java
+++ b/core/java/android/pim/vcard/VCardDataBuilder.java
@@ -69,7 +69,7 @@ public class VCardDataBuilder implements VCardBuilder {
private List<EntryHandler> mEntryHandlers = new ArrayList<EntryHandler>();
public VCardDataBuilder() {
- this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC, null);
+ this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, null);
}
/**
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index b3bf426..376327c 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -18,6 +18,7 @@ package android.pim.vcard;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.text.TextUtils;
@@ -44,38 +45,49 @@ public class VCardUtils {
private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
private static final Set<String> sPhoneTypesSetUnknownToContacts;
- private static final Map<String, Integer> sKnownPhoneTypesMap_StoI;
+ private static final Map<String, Integer> sKnownPhoneTypeMap_StoI;
+
+ private static final Map<Integer, String> sKnownImPropNameMap_ItoS;
static {
sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
- sKnownPhoneTypesMap_StoI = new HashMap<String, Integer>();
+ sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>();
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, Constants.ATTR_TYPE_CAR);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, Constants.ATTR_TYPE_PAGER);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, Constants.ATTR_TYPE_ISDN);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_CALLBACK, Phone.TYPE_CALLBACK);
- sKnownPhoneTypesMap_StoI.put(
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_CALLBACK, Phone.TYPE_CALLBACK);
+ sKnownPhoneTypeMap_StoI.put(
Constants.ATTR_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_TELEX, Phone.TYPE_TELEX);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_TTY_TDD, Phone.TYPE_TTY_TDD);
- sKnownPhoneTypesMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_ASSISTANT,
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_TTY_TDD, Phone.TYPE_TTY_TDD);
+ sKnownPhoneTypeMap_StoI.put(Constants.ATTR_PHONE_EXTRA_TYPE_ASSISTANT,
Phone.TYPE_ASSISTANT);
sPhoneTypesSetUnknownToContacts = new HashSet<String>();
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MODEM);
- sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MSG);
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_BBS);
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_VIDEO);
+
+ sKnownImPropNameMap_ItoS = new HashMap<Integer, String>();
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, Constants.PROPERTY_X_AIM);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, Constants.PROPERTY_X_MSN);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, Constants.PROPERTY_X_YAHOO);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, Constants.PROPERTY_X_SKYPE_USERNAME);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK, Constants.PROPERTY_X_GOOGLE_TALK);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, Constants.PROPERTY_X_ICQ);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, Constants.PROPERTY_X_JABBER);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, Constants.PROPERTY_X_QQ);
+ sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, Constants.PROPERTY_X_NETMEETING);
}
public static String getPhoneAttributeString(Integer type) {
@@ -103,7 +115,7 @@ public class VCardUtils {
if (typeString.startsWith("X-") && type < 0) {
typeString = typeString.substring(2);
}
- Integer tmp = sKnownPhoneTypesMap_StoI.get(typeString);
+ Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString);
if (tmp != null) {
type = tmp;
} else if (type < 0) {
@@ -136,7 +148,11 @@ public class VCardUtils {
return type;
}
}
-
+
+ public static String getPropertyNameForIm(int protocol) {
+ return sKnownImPropNameMap_ItoS.get(protocol);
+ }
+
public static boolean isValidPhoneAttribute(String phoneAttribute, int vcardType) {
// TODO: check the following.
// - it may violate vCard spec
@@ -206,12 +222,12 @@ public class VCardUtils {
builder.withValue(Data.IS_PRIMARY, 1);
}
}
-
+
/**
* Returns String[] containing address information based on vCard spec
* (PO Box, Extended Address, Street, Locality, Region, Postal Code, Country Name).
* All String objects are non-null ("" is used when the relevant data is empty).
- *
+ *
* Note that the data structure of ContactsContract is different from that defined in vCard.
* So some conversion may be performed in this method. See also
* {{@link #insertStructuredPostalDataUsingContactsStruct(int,
@@ -219,13 +235,20 @@ public class VCardUtils {
* android.pim.vcard.ContactStruct.PostalData)}
*/
public static String[] getVCardPostalElements(ContentValues contentValues) {
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal
+ // ; Code, Country Name
String[] dataArray = new String[7];
dataArray[0] = contentValues.getAsString(StructuredPostal.POBOX);
if (dataArray[0] == null) {
dataArray[0] = "";
}
- // Extended addr. There's no relevant data in ContactsContract.
- dataArray[1] = "";
+ // We keep all the data in StructuredPostal, presuming NEIGHBORHOOD is
+ // similar to "Extended Address".
+ dataArray[1] = contentValues.getAsString(StructuredPostal.NEIGHBORHOOD);
+ if (dataArray[1] == null) {
+ dataArray[1] = "";
+ }
dataArray[2] = contentValues.getAsString(StructuredPostal.STREET);
if (dataArray[2] == null) {
dataArray[2] = "";
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
index 3acd6c1..b009e2d 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
@@ -230,6 +230,6 @@ public class PropertyNodesVerifier {
}
mTestCase.fail("Property \"" + propName + "\" has wrong value.\n"
+ builder.toString()
- + " actual: " + actualNode.toString());
+ + " actual: " + actualNode.toString());
}
}
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 3f10605..976474f 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardExporterTests.java
@@ -44,8 +44,17 @@ import android.pim.vcard.exception.VCardException;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
import android.test.AndroidTestCase;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
@@ -147,7 +156,10 @@ class MockContentProvider extends ContentProvider {
* the result of this class will not be reliable.
*/
public class VCardExporterTests extends AndroidTestCase {
- /* package */ static final byte[] sPhotoByteArray =
+ private static final int V21 = 0;
+ private static final int V30 = 1;
+
+ private static final byte[] sPhotoByteArray =
VCardImporterTests.sPhotoByteArrayForComplicatedCase;
public class ExportTestResolver extends MockContentResolver {
@@ -282,17 +294,26 @@ public class VCardExporterTests extends AndroidTestCase {
final private boolean mIsV30;
int mCount;
- public VCardVerificationHandler(TestCase testCase, boolean isV30) {
+ public VCardVerificationHandler(TestCase testCase, int version) {
mTestCase = testCase;
mPropertyNodesVerifierList = new ArrayList<PropertyNodesVerifier>();
- mIsV30 = isV30;
+ mIsV30 = (version == V30);
mCount = 1;
}
- public PropertyNodesVerifier addNewPropertyNodesVerifier() {
- PropertyNodesVerifier propertyNodesVerifier = new PropertyNodesVerifier(mTestCase);
- mPropertyNodesVerifierList.add(propertyNodesVerifier);
- return propertyNodesVerifier;
+ public PropertyNodesVerifier addNewVerifier() {
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(mTestCase);
+ mPropertyNodesVerifierList.add(verifier);
+ verifier.addNodeWithOrder("VERSION", mIsV30 ? "3.0" : "2.1");
+ return verifier;
+ }
+
+ public PropertyNodesVerifier addNewVerifierWithEmptyName() {
+ PropertyNodesVerifier verifier = addNewVerifier();
+ if (mIsV30) {
+ verifier.addNodeWithOrder("N", "").addNodeWithOrder("FN", "");
+ }
+ return verifier;
}
public boolean onInit(Context context) {
@@ -344,31 +365,823 @@ public class VCardExporterTests extends AndroidTestCase {
//// Followings are actual tests ////
- public void testSimple() {
+ private void verifyOneComposition(ExportTestResolver resolver,
+ VCardVerificationHandler handler, int version) {
+ final boolean isV30 = (version == V30);
+
+ int vcardType = (isV30 ? VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8
+ : VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ VCardComposer composer = new VCardComposer(new CustomMockContext(resolver), vcardType);
+ composer.addHandler(handler);
+ if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
+ fail("init() failed. Reason: " + composer.getErrorReason());
+ }
+ assertFalse(composer.isAfterLast());
+ assertTrue(composer.createOneEntry());
+ assertTrue(composer.isAfterLast());
+ composer.terminate();
+ }
+
+ public void testSimpleV21() {
ExportTestResolver resolver = new ExportTestResolver();
ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "Ando");
contentValues.put(StructuredName.GIVEN_NAME, "Roid");
- VCardVerificationHandler handler = new VCardVerificationHandler(this, false);
- handler.addNewPropertyNodesVerifier()
- .addNodeWithOrder("VERSION", "2.1")
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, V21);
+ handler.addNewVerifier()
.addNodeWithoutOrder("FN", "Roid Ando")
.addNodeWithoutOrder("N", "Ando;Roid;;;", Arrays.asList("Ando", "Roid", "", "", ""));
- VCardComposer composer = new VCardComposer(new CustomMockContext(resolver),
- VCardConfig.VCARD_TYPE_V21_GENERIC);
- composer.addHandler(handler);
- if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
- fail("init failed. Reason: " + composer.getErrorReason());
+ verifyOneComposition(resolver, handler, V21);
+ }
+
+ private void testStructuredNameBasic(int version) {
+ final boolean isV30 = (version == V30);
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName");
+ contentValues.put(StructuredName.GIVEN_NAME, "AppropriateGivenName");
+ contentValues.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName");
+ contentValues.put(StructuredName.PREFIX, "AppropriatePrefix");
+ contentValues.put(StructuredName.SUFFIX, "AppropriateSuffix");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ PropertyNodesVerifier verifier = handler.addNewVerifier()
+ .addNodeWithOrder("N",
+ "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+ + "AppropriatePrefix;AppropriateSuffix",
+ Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+ "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+ .addNodeWithOrder("FN",
+ "AppropriatePrefix AppropriateGivenName "
+ + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+ .addNodeWithoutOrder("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+ .addNodeWithoutOrder("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+ .addNodeWithoutOrder("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+ if (isV30) {
+ verifier.addNodeWithoutOrder("SORT-STRING",
+ "AppropriatePhoneticGiven AppropriatePhoneticMiddle "
+ + "AppropriatePhoneticFamily");
}
- assertFalse(composer.isAfterLast());
- assertTrue(composer.createOneEntry());
- assertTrue(composer.isAfterLast());
- composer.terminate();
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testStructuredNameBasicV21() {
+ testStructuredNameBasic(V21);
+ }
+
+ public void testStructuredNameBasicV30() {
+ testStructuredNameBasic(V30);
+ }
+
+ /**
+ * Test that only "primary" StructuredName is emitted, so that our vCard file
+ * will not confuse the external importer, assuming there may be some importer
+ * which presume that there's only one property toward each of "N", "FN", etc.
+ * Note that more than one "N", "FN", etc. properties are acceptable in vCard spec.
+ */
+ private void testStructuredNameUsePrimaryCommon(int version) {
+ final boolean isV30 = (version == V30);
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix1");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix1");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
+
+ // With "IS_PRIMARY=1". This is what we should use.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName");
+ contentValues.put(StructuredName.GIVEN_NAME, "AppropriateGivenName");
+ contentValues.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName");
+ contentValues.put(StructuredName.PREFIX, "AppropriatePrefix");
+ contentValues.put(StructuredName.SUFFIX, "AppropriateSuffix");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix2");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix2");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ PropertyNodesVerifier verifier = handler.addNewVerifier()
+ .addNodeWithOrder("N",
+ "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+ + "AppropriatePrefix;AppropriateSuffix",
+ Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+ "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+ .addNodeWithOrder("FN",
+ "AppropriatePrefix AppropriateGivenName "
+ + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+ .addNodeWithoutOrder("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+ .addNodeWithoutOrder("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+ .addNodeWithoutOrder("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+ if (isV30) {
+ verifier.addNodeWithoutOrder("SORT-STRING",
+ "AppropriatePhoneticGiven AppropriatePhoneticMiddle "
+ + "AppropriatePhoneticFamily");
+ }
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testStructuredNameUsePrimaryV21() {
+ testStructuredNameUsePrimaryCommon(V21);
+ }
+
+ public void testStructuredNameUsePrimaryV30() {
+ testStructuredNameUsePrimaryCommon(V30);
+ }
+
+ /**
+ * Tests that only "super primary" StructuredName is emitted.
+ * See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
+ */
+ private void testStructuredNameUseSuperPrimaryCommon(int version) {
+ final boolean isV30 = (version == V30);
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix1");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix1");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
+
+ // With "IS_PRIMARY=1", but we should ignore this time.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix2");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix2");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ // With "IS_SUPER_PRIMARY=1". This is what we should use.
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName");
+ contentValues.put(StructuredName.GIVEN_NAME, "AppropriateGivenName");
+ contentValues.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName");
+ contentValues.put(StructuredName.PREFIX, "AppropriatePrefix");
+ contentValues.put(StructuredName.SUFFIX, "AppropriateSuffix");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
+ contentValues.put(StructuredName.IS_SUPER_PRIMARY, 1);
+
+ contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3");
+ contentValues.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3");
+ contentValues.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3");
+ contentValues.put(StructuredName.PREFIX, "DoNotEmitPrefix3");
+ contentValues.put(StructuredName.SUFFIX, "DoNotEmitSuffix3");
+ contentValues.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily3");
+ contentValues.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven3");
+ contentValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3");
+ contentValues.put(StructuredName.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ PropertyNodesVerifier verifier = handler.addNewVerifier()
+ .addNodeWithOrder("N",
+ "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+ + "AppropriatePrefix;AppropriateSuffix",
+ Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+ "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+ .addNodeWithOrder("FN",
+ "AppropriatePrefix AppropriateGivenName "
+ + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+ .addNodeWithoutOrder("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+ .addNodeWithoutOrder("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+ .addNodeWithoutOrder("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+ if (isV30) {
+ verifier.addNodeWithoutOrder("SORT-STRING",
+ "AppropriatePhoneticGiven AppropriatePhoneticMiddle"
+ + " AppropriatePhoneticFamily");
+ }
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testStructuredNameUseSuperPrimaryV21() {
+ testStructuredNameUseSuperPrimaryCommon(V21);
+ }
+
+ public void testStructuredNameUseSuperPrimaryV30() {
+ 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);
+ contentValues.put(Nickname.NAME, "Nicky");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, V30);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("NICKNAME", "Nicky");
+
+ verifyOneComposition(resolver, handler, V30);
+ }
+
+ private void testPhoneBasicCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "1");
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("TEL", "1", new TypeSet("HOME", "VOICE"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPhoneBasicV21() {
+ testPhoneBasicCommon(V21);
+ }
+
+ public void testPhoneBasicV30() {
+ testPhoneBasicCommon(V30);
+ }
+
+ /**
+ * Tests that vCard composer emits corresponding type param which we expect.
+ */
+ private void testPhoneVariousTypeSupport(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "10");
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "20");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "30");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_HOME);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "40");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "50");
+ contentValues.put(Phone.TYPE, Phone.TYPE_MOBILE);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "60");
+ contentValues.put(Phone.TYPE, Phone.TYPE_PAGER);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "70");
+ contentValues.put(Phone.TYPE, Phone.TYPE_OTHER);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "80");
+ contentValues.put(Phone.TYPE, Phone.TYPE_CAR);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "90");
+ contentValues.put(Phone.TYPE, Phone.TYPE_COMPANY_MAIN);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "100");
+ contentValues.put(Phone.TYPE, Phone.TYPE_ISDN);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "110");
+ contentValues.put(Phone.TYPE, Phone.TYPE_MAIN);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "120");
+ contentValues.put(Phone.TYPE, Phone.TYPE_OTHER_FAX);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "130");
+ contentValues.put(Phone.TYPE, Phone.TYPE_TELEX);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "140");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "150");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK_PAGER);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "160");
+ contentValues.put(Phone.TYPE, Phone.TYPE_MMS);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("TEL", "10", new TypeSet("HOME"))
+ .addNodeWithoutOrder("TEL", "20", new TypeSet("WORK"))
+ .addNodeWithoutOrder("TEL", "30", new TypeSet("HOME", "FAX"))
+ .addNodeWithoutOrder("TEL", "40", new TypeSet("WORK", "FAX"))
+ .addNodeWithoutOrder("TEL", "50", new TypeSet("CELL"))
+ .addNodeWithoutOrder("TEL", "60", new TypeSet("PAGER"))
+ .addNodeWithoutOrder("TEL", "70", new TypeSet("VOICE"))
+ .addNodeWithoutOrder("TEL", "80", new TypeSet("CAR"))
+ .addNodeWithoutOrder("TEL", "90", new TypeSet("WORK", "PREF"))
+ .addNodeWithoutOrder("TEL", "100", new TypeSet("ISDN"))
+ .addNodeWithoutOrder("TEL", "110", new TypeSet("PREF"))
+ .addNodeWithoutOrder("TEL", "120", new TypeSet("FAX"))
+ .addNodeWithoutOrder("TEL", "130", new TypeSet("TLX"))
+ .addNodeWithoutOrder("TEL", "140", new TypeSet("WORK", "MOBILE"))
+ .addNodeWithoutOrder("TEL", "150", new TypeSet("WORK", "PAGER"))
+ .addNodeWithoutOrder("TEL", "160", new TypeSet("MSG"));
+ }
+
+ public void testPhoneVariousTypeSupportV21() {
+ testPhoneVariousTypeSupport(V21);
+ }
+
+ public void testPhoneVariousTypeSupportV30() {
+ testPhoneVariousTypeSupport(V30);
+ }
+
+ /**
+ * Tests that "PREF"s are emitted appropriately.
+ */
+ private void testPhonePrefHandlingCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "1");
+ contentValues.put(Phone.TYPE, Phone.TYPE_HOME);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "2");
+ contentValues.put(Phone.TYPE, Phone.TYPE_WORK);
+ contentValues.put(Phone.IS_PRIMARY, 1);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "3");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_HOME);
+ contentValues.put(Phone.IS_PRIMARY, 1);
+
+ contentValues = resolver.buildData(Phone.CONTENT_ITEM_TYPE);
+ contentValues.put(Phone.NUMBER, "4");
+ contentValues.put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("TEL", "4", new TypeSet("WORK", "FAX"))
+ .addNodeWithoutOrder("TEL", "3", new TypeSet("HOME", "FAX", "PREF"))
+ .addNodeWithoutOrder("TEL", "2", new TypeSet("WORK", "VOICE", "PREF"))
+ .addNodeWithoutOrder("TEL", "1", new TypeSet("HOME", "VOICE"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPhonePrefHandlingV21() {
+ testPhonePrefHandlingCommon(V21);
+ }
+
+ public void testPhonePrefHandlingV30() {
+ testPhonePrefHandlingCommon(V30);
+ }
+
+ private void testEmailBasicCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "sample@example.com");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("EMAIL", "sample@example.com");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testEmailBasicV21() {
+ testEmailBasicCommon(V21);
+ }
+
+ public void testEmailBasicV30() {
+ testEmailBasicCommon(V30);
+ }
+
+ private void testEmailVariousTypeSupportCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_home@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_HOME);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_work@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_WORK);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_mobile@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_MOBILE);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_other@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_OTHER);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("EMAIL", "type_home@example.com", new TypeSet("HOME"))
+ .addNodeWithoutOrder("EMAIL", "type_work@example.com", new TypeSet("WORK"))
+ .addNodeWithoutOrder("EMAIL", "type_mobile@example.com", new TypeSet("CELL"))
+ .addNodeWithoutOrder("EMAIL", "type_other@example.com");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testEmailVariousTypeSupportV21() {
+ testEmailVariousTypeSupportCommon(V21);
+ }
+
+ public void testEmailVariousTypeSupportV30() {
+ testEmailVariousTypeSupportCommon(V30);
+ }
+
+ private void testEmailPrefHandlingCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_home@example.com");
+ contentValues.put(Email.TYPE, Email.TYPE_HOME);
+ contentValues.put(Email.IS_PRIMARY, 1);
+
+ contentValues = resolver.buildData(Email.CONTENT_ITEM_TYPE);
+ contentValues.put(Email.DATA, "type_notype@example.com");
+ contentValues.put(Email.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("EMAIL", "type_notype@example.com", new TypeSet("PREF"))
+ .addNodeWithoutOrder("EMAIL", "type_home@example.com", new TypeSet("HOME", "PREF"));
+
+ verifyOneComposition(resolver, handler, version);
}
- private void testPhotoCommon(boolean isV30) {
+ public void testEmailPrefHandlingV21() {
+ testEmailPrefHandlingCommon(V21);
+ }
+
+ public void testEmailPrefHandlingV30() {
+ testEmailPrefHandlingCommon(V30);
+ }
+
+ private void testPostalOnlyWithStructuredDataCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal Code,
+ // ; Country Name
+ ContentValues contentValues = resolver.buildData(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.POBOX, "Pobox");
+ contentValues.put(StructuredPostal.NEIGHBORHOOD, "Neighborhood");
+ contentValues.put(StructuredPostal.STREET, "Street");
+ contentValues.put(StructuredPostal.CITY, "City");
+ contentValues.put(StructuredPostal.REGION, "Region");
+ contentValues.put(StructuredPostal.POSTCODE, "100");
+ contentValues.put(StructuredPostal.COUNTRY, "Country");
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("ADR", "Pobox;Neighborhood;Street;City;Region;100;Country",
+ Arrays.asList("Pobox", "Neighborhood", "Street", "City",
+ "Region", "100", "Country"), new TypeSet("HOME"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPostalOnlyWithStructuredDataV21() {
+ testPostalOnlyWithStructuredDataCommon(V21);
+ }
+
+ public void testPostalOnlyWithStructuredDataV30() {
+ testPostalOnlyWithStructuredDataCommon(V30);
+ }
+
+ private void testPostalOnlyWithFormattedAddressCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.FORMATTED_ADDRESS,
+ "Formatted address CA 123-334 United Statue");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("ADR", ";Formatted address CA 123-334 United Statue;;;;;",
+ Arrays.asList("", "Formatted address CA 123-334 United Statue",
+ "", "", "", "", ""), new TypeSet("HOME"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPostalOnlyWithFormattedAddressV21() {
+ testPostalOnlyWithFormattedAddressCommon(V21);
+ }
+
+ public void testPostalOnlyWithFormattedAddressV30() {
+ testPostalOnlyWithFormattedAddressCommon(V30);
+ }
+
+ /**
+ * Tests that the vCard composer honors formatted data when it is available
+ * even when it is partial.
+ */
+ private void testPostalWithBothStructuredAndFormattedCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(StructuredPostal.CONTENT_ITEM_TYPE);
+ contentValues.put(StructuredPostal.POBOX, "Pobox");
+ contentValues.put(StructuredPostal.COUNTRY, "Country");
+ contentValues.put(StructuredPostal.FORMATTED_ADDRESS,
+ "Formatted address CA 123-334 United Statue"); // Should be ignored
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("ADR", "Pobox;;;;;;Country",
+ Arrays.asList("Pobox", "", "", "", "", "", "Country"), new TypeSet("HOME"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testPostalWithBothStructuredAndFormattedV21() {
+ testPostalWithBothStructuredAndFormattedCommon(V21);
+ }
+
+ public void testPostalWithBothStructuredAndFormattedV30() {
+ testPostalWithBothStructuredAndFormattedCommon(V30);
+ }
+
+ private void testOrganizationCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+ ContentValues contentValues = resolver.buildData(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "CompanyX");
+ contentValues.put(Organization.DEPARTMENT, "DepartmentY");
+ contentValues.put(Organization.TITLE, "TitleZ");
+ contentValues.put(Organization.JOB_DESCRIPTION, "Description Rambda"); // Ignored.
+ contentValues.put(Organization.OFFICE_LOCATION, "Mountain View"); // Ignored.
+ contentValues.put(Organization.PHONETIC_NAME, "PhoneticName!"); // Ignored
+ contentValues.put(Organization.SYMBOL, "(^o^)/~~"); // Ignore him (her).
+
+ contentValues = resolver.buildData(Organization.CONTENT_ITEM_TYPE);
+ contentValues.putNull(Organization.COMPANY);
+ contentValues.put(Organization.DEPARTMENT, "DepartmentXX");
+ contentValues.putNull(Organization.TITLE);
+
+ contentValues = resolver.buildData(Organization.CONTENT_ITEM_TYPE);
+ contentValues.put(Organization.COMPANY, "CompanyXYZ");
+ contentValues.putNull(Organization.DEPARTMENT);
+ contentValues.put(Organization.TITLE, "TitleXYZYX");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+
+ // Currently we do not use group but depend on the order.
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("ORG", "CompanyX;DepartmentY",
+ Arrays.asList("CompanyX", "DepartmentY"))
+ .addNodeWithOrder("TITLE", "TitleZ")
+ .addNodeWithOrder("ORG", "DepartmentXX")
+ .addNodeWithOrder("ORG", "CompanyXYZ")
+ .addNodeWithOrder("TITLE", "TitleXYZYX");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testOrganizationV21() {
+ testOrganizationCommon(V21);
+ }
+
+ public void testOrganizationV30() {
+ testOrganizationCommon(V30);
+ }
+
+ private void testImVariousTypeSupportCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_AIM);
+ contentValues.put(Im.DATA, "aim");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_MSN);
+ contentValues.put(Im.DATA, "msn");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO);
+ contentValues.put(Im.DATA, "yahoo");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_SKYPE);
+ contentValues.put(Im.DATA, "skype");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_QQ);
+ contentValues.put(Im.DATA, "qq");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
+ contentValues.put(Im.DATA, "google talk");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_ICQ);
+ contentValues.put(Im.DATA, "icq");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_JABBER);
+ contentValues.put(Im.DATA, "jabber");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_NETMEETING);
+ contentValues.put(Im.DATA, "netmeeting");
+
+ // No determined way to express unknown type...
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("X-JABBER", "jabber")
+ .addNodeWithoutOrder("X-ICQ", "icq")
+ .addNodeWithoutOrder("X-GOOGLE-TALK", "google talk")
+ .addNodeWithoutOrder("X-QQ", "qq")
+ .addNodeWithoutOrder("X-SKYPE-USERNAME", "skype")
+ .addNodeWithoutOrder("X-YAHOO", "yahoo")
+ .addNodeWithoutOrder("X-MSN", "msn")
+ .addNodeWithoutOrder("X-NETMEETING", "netmeeting")
+ .addNodeWithoutOrder("X-AIM", "aim");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testImBasiV21() {
+ testImVariousTypeSupportCommon(V21);
+ }
+
+ public void testImBasicV30() {
+ testImVariousTypeSupportCommon(V30);
+ }
+
+ private void testImPrefHandlingCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_AIM);
+ contentValues.put(Im.DATA, "aim1");
+
+ contentValues = resolver.buildData(Im.CONTENT_ITEM_TYPE);
+ contentValues.put(Im.PROTOCOL, Im.PROTOCOL_AIM);
+ contentValues.put(Im.DATA, "aim2");
+ contentValues.put(Im.TYPE, Im.TYPE_HOME);
+ contentValues.put(Im.IS_PRIMARY, 1);
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("X-AIM", "aim1")
+ .addNodeWithoutOrder("X-AIM", "aim2", new TypeSet("HOME", "PREF"));
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testImPrefHandlingV21() {
+ testImPrefHandlingCommon(V21);
+ }
+
+ public void testImPrefHandlingV30() {
+ testImPrefHandlingCommon(V30);
+ }
+
+ private void testWebsiteCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Website.CONTENT_ITEM_TYPE);
+ contentValues.put(Website.URL, "http://website.example.android.com/index.html");
+ contentValues.put(Website.TYPE, Website.TYPE_BLOG);
+
+ contentValues = resolver.buildData(Website.CONTENT_ITEM_TYPE);
+ contentValues.put(Website.URL, "ftp://ftp.example.android.com/index.html");
+ contentValues.put(Website.TYPE, Website.TYPE_FTP);
+
+ // We drop TYPE information since vCard (especially 3.0) does not allow us to emit it.
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("URL", "ftp://ftp.example.android.com/index.html")
+ .addNodeWithoutOrder("URL", "http://website.example.android.com/index.html");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testWebsiteV21() {
+ testWebsiteCommon(V21);
+ }
+
+ public void testWebsiteV30() {
+ testWebsiteCommon(V30);
+ }
+
+ private void testEventCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY);
+ contentValues.put(Event.START_DATE, "1982-06-16");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+ contentValues.put(Event.START_DATE, "2008-10-22");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_OTHER);
+ contentValues.put(Event.START_DATE, "2018-03-12");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_CUSTOM);
+ contentValues.put(Event.LABEL, "The last day");
+ contentValues.put(Event.START_DATE, "When the Tower of Hanoi with 64 rings is completed.");
+
+ contentValues = resolver.buildData(Event.CONTENT_ITEM_TYPE);
+ contentValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+ contentValues.put(Event.START_DATE, "2009-05-19");
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithoutOrder("BDAY", "2008-10-22");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testEventV21() {
+ testEventCommon(V21);
+ }
+
+ public void testEventV30() {
+ testEventCommon(V30);
+ }
+
+ private void testNoteCommon(int version) {
+ ExportTestResolver resolver = new ExportTestResolver();
+
+ ContentValues contentValues = resolver.buildData(Note.CONTENT_ITEM_TYPE);
+ contentValues.put(Note.NOTE, "note1");
+
+ contentValues = resolver.buildData(Note.CONTENT_ITEM_TYPE);
+ contentValues.put(Note.NOTE, "note2");
+ contentValues.put(Note.IS_PRIMARY, 1); // Just ignored.
+
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifierWithEmptyName()
+ .addNodeWithOrder("NOTE", "note1")
+ .addNodeWithOrder("NOTE", "note2");
+
+ verifyOneComposition(resolver, handler, version);
+ }
+
+ public void testNoteV21() {
+ testNoteCommon(V21);
+ }
+
+ public void testNoteV30() {
+ testNoteCommon(V30);
+ }
+
+ // TODO: test for non-ascii...
+
+ private void testPhotoCommon(int version) {
+ final boolean isV30 = version == V30;
ExportTestResolver resolver = new ExportTestResolver();
ContentValues contentValues = resolver.buildData(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "PhotoTest");
@@ -378,32 +1191,21 @@ public class VCardExporterTests extends AndroidTestCase {
ContentValues contentValuesForPhoto = new ContentValues();
contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64"));
- VCardVerificationHandler handler = new VCardVerificationHandler(this, isV30);
- handler.addNewPropertyNodesVerifier()
- .addNodeWithOrder("VERSION", (isV30 ? "3.0" : "2.1"))
+ VCardVerificationHandler handler = new VCardVerificationHandler(this, version);
+ handler.addNewVerifier()
.addNodeWithoutOrder("FN", "PhotoTest")
.addNodeWithoutOrder("N", "PhotoTest;;;;", Arrays.asList("PhotoTest", "", "", "", ""))
.addNodeWithOrder("PHOTO", null, null, sPhotoByteArray,
contentValuesForPhoto, new TypeSet("JPEG"), null);
- int vcardType = (isV30 ? VCardConfig.VCARD_TYPE_V30_GENERIC
- : VCardConfig.VCARD_TYPE_V21_GENERIC);
- VCardComposer composer = new VCardComposer(new CustomMockContext(resolver), vcardType);
- composer.addHandler(handler);
- if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
- fail("init() failed. Reason: " + composer.getErrorReason());
- }
- assertFalse(composer.isAfterLast());
- assertTrue(composer.createOneEntry());
- assertTrue(composer.isAfterLast());
- composer.terminate();
+ verifyOneComposition(resolver, handler, version);
}
public void testPhotoV21() {
- testPhotoCommon(false);
+ testPhotoCommon(V21);
}
public void testPhotoV30() {
- testPhotoCommon(true);
+ testPhotoCommon(V30);
}
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
index c6f827d..51bce72 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
@@ -722,7 +722,7 @@ public class VCardImporterTests extends AndroidTestCase {
public void testV21SimpleCase1_Type_Generic() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_simple_1, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_simple_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "Ando");
@@ -733,7 +733,7 @@ public class VCardImporterTests extends AndroidTestCase {
public void testV21SimpleCase1_Type_Japanese() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_simple_1, VCardConfig.VCARD_TYPE_V21_JAPANESE);
+ R.raw.v21_simple_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "Ando");
@@ -746,7 +746,7 @@ public class VCardImporterTests extends AndroidTestCase {
public void testV21SimpleCase2() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_simple_2, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_simple_2, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.DISPLAY_NAME, "Ando Roid");
@@ -755,7 +755,7 @@ public class VCardImporterTests extends AndroidTestCase {
public void testV21SimpleCase3() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_simple_3, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_simple_3, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "Ando");
@@ -790,7 +790,7 @@ public class VCardImporterTests extends AndroidTestCase {
*/
public void testV21BackslashCase() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_backslash, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_backslash, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
// FAMILY_NAME is empty and removed in this test...
@@ -804,7 +804,7 @@ public class VCardImporterTests extends AndroidTestCase {
public void testOrgBeforTitle() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_org_before_title, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_org_before_title, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.DISPLAY_NAME, "Normal Guy");
@@ -819,7 +819,7 @@ public class VCardImporterTests extends AndroidTestCase {
public void testTitleBeforOrg() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_title_before_org, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_title_before_org, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.DISPLAY_NAME, "Nice Guy");
@@ -838,7 +838,7 @@ public class VCardImporterTests extends AndroidTestCase {
*/
public void testV21PrefToIsPrimary() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_pref_handling, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_pref_handling, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
@@ -952,7 +952,7 @@ public class VCardImporterTests extends AndroidTestCase {
*/
public void testV21ComplicatedCase() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_complicated, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_complicated, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "Gump");
@@ -1068,7 +1068,7 @@ public class VCardImporterTests extends AndroidTestCase {
public void testV30Simple() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v30_simple, VCardConfig.VCARD_TYPE_V30_GENERIC);
+ R.raw.v30_simple, VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "And");
@@ -1139,25 +1139,28 @@ public class VCardImporterTests extends AndroidTestCase {
verifier.verify();
}
/**
- * Verifies vCard with Japanese can be parsed correctly with VCARD_TYPE_V21_GENERIC.
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC_UTF8}.
*/
- public void testV21Japanese1_Type_Generic() throws IOException, VCardException {
+ public void testV21Japanese1_Type_Generic_Utf8() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
testV21Japanese1Common(verifier, false);
}
/**
- * Verifies vCard with Japanese can be parsed correctly with VCARD_TYPE_V21_JAPANESE.
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_SJIS}.
*/
- public void testV21Japanese1_Type_Japanese() throws IOException, VCardException {
+ public void testV21Japanese1_Type_Japanese_Sjis() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE);
+ R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
testV21Japanese1Common(verifier, true);
}
/**
- * Verifies vCard with Japanese can be parsed correctly with VCARD_TYPE_V21_JAPANESE_UTF8,
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_UTF8}.
* since vCard 2.1 specifies the charset of each line if it contains non-Ascii.
*/
public void testV21Japanese1_Type_Japanese_Utf8() throws IOException, VCardException {
@@ -1205,9 +1208,9 @@ public class VCardImporterTests extends AndroidTestCase {
verifier.verify(builder.vNodeList.get(0));
}
- public void testV21Japanese2_Type_Generic() throws IOException, VCardException {
+ public void testV21Japanese2_Type_Generic_Utf8() throws IOException, VCardException {
ContactStructVerifier verifier = new ContactStructVerifier(
- R.raw.v21_japanese_2, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ R.raw.v21_japanese_2, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
ContentValues contentValues =
verifier.createExpected(StructuredName.CONTENT_ITEM_TYPE);
contentValues.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4");