am a39f9188: Merge change I9c45753b into eclair
Merge commit 'a39f9188d6c1cdcacef2ab468fd78376171a3447' into eclair-plus-aosp * commit 'a39f9188d6c1cdcacef2ab468fd78376171a3447': Clean up vCard code.
This commit is contained in:
@@ -1051,8 +1051,8 @@ public class ContactStruct {
|
||||
List<String> nameList;
|
||||
switch (VCardConfig.getNameOrderType(mVCardType)) {
|
||||
case VCardConfig.NAME_ORDER_JAPANESE:
|
||||
if (VCardUtils.containsOnlyAscii(mFamilyName) &&
|
||||
VCardUtils.containsOnlyAscii(mGivenName)) {
|
||||
if (VCardUtils.containsOnlyPrintableAscii(mFamilyName) &&
|
||||
VCardUtils.containsOnlyPrintableAscii(mGivenName)) {
|
||||
nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix);
|
||||
} else {
|
||||
nameList = Arrays.asList(mPrefix, mFamilyName, mMiddleName, mGivenName, mSuffix);
|
||||
|
||||
@@ -56,8 +56,10 @@ import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -234,9 +236,8 @@ public class VCardComposer {
|
||||
private static final String VCARD_PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
|
||||
|
||||
// Android specific properties
|
||||
private static final String VCARD_PROPERTY_X_PHONETIC_NAME = "X-PHONETIC-NAME";
|
||||
// TODO: ues extra MIME-TYPE instead of adding this kind of inflexible fields
|
||||
private static final String VCARD_PROPERTY_X_NICKNAME = "X-NICKNAME";
|
||||
// TODO: add properties like X-LATITUDE
|
||||
|
||||
// Property for call log entry
|
||||
private static final String VCARD_PROPERTY_X_TIMESTAMP = "X-IRMC-CALL-DATETIME";
|
||||
@@ -396,7 +397,7 @@ public class VCardComposer {
|
||||
}
|
||||
|
||||
boolean needCharset = false;
|
||||
if (!(VCardUtils.containsOnlyAscii(phoneName))) {
|
||||
if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) {
|
||||
needCharset = true;
|
||||
}
|
||||
appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, phoneName, needCharset, false);
|
||||
@@ -596,7 +597,7 @@ public class VCardComposer {
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
name = mCursor.getString(NUMBER_COLUMN_INDEX);
|
||||
}
|
||||
final boolean needCharset = !(VCardUtils.containsOnlyAscii(name));
|
||||
final boolean needCharset = !(VCardUtils.containsOnlyPrintableAscii(name));
|
||||
appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, name, needCharset, false);
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NAME, name, needCharset, false);
|
||||
|
||||
@@ -740,213 +741,220 @@ public class VCardComposer {
|
||||
final Map<String, List<ContentValues>> contentValuesListMap) {
|
||||
final List<ContentValues> contentValuesList = contentValuesListMap
|
||||
.get(StructuredName.CONTENT_ITEM_TYPE);
|
||||
if (contentValuesList != null) {
|
||||
if (contentValuesList != null && contentValuesList.size() > 0) {
|
||||
appendStructuredNamesInternal(builder, contentValuesList);
|
||||
} else if (mIsDoCoMo) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
|
||||
} else if (mIsV30) {
|
||||
// vCard 3.0 requires "N" and "FN" properties.
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
|
||||
appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, "");
|
||||
}
|
||||
}
|
||||
|
||||
private void appendStructuredNamesInternal(final StringBuilder builder,
|
||||
final List<ContentValues> contentValuesList) {
|
||||
// For safety, we'll emit just one value around StructuredName, as external importers
|
||||
// may get confused with multiple "N", "FN", etc. properties, though it is valid in
|
||||
// vCard spec.
|
||||
ContentValues primaryContentValues = null;
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
final String familyName = contentValues
|
||||
.getAsString(StructuredName.FAMILY_NAME);
|
||||
final String middleName = contentValues
|
||||
.getAsString(StructuredName.MIDDLE_NAME);
|
||||
final String givenName = contentValues
|
||||
.getAsString(StructuredName.GIVEN_NAME);
|
||||
final String prefix = contentValues
|
||||
.getAsString(StructuredName.PREFIX);
|
||||
final String suffix = contentValues
|
||||
.getAsString(StructuredName.SUFFIX);
|
||||
final String displayName = contentValues
|
||||
.getAsString(StructuredName.DISPLAY_NAME);
|
||||
Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// For now, some primary element is not encoded into Quoted-Printable, which is not
|
||||
// valid in vCard spec strictly. In the future, we may have to have some flag to
|
||||
// enable composer to encode these primary field into Quoted-Printable.
|
||||
if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) {
|
||||
final String encodedFamily = escapeCharacters(familyName);
|
||||
final String encodedGiven = escapeCharacters(givenName);
|
||||
final String encodedMiddle = escapeCharacters(middleName);
|
||||
final String encodedPrefix = escapeCharacters(prefix);
|
||||
final String encodedSuffix = escapeCharacters(suffix);
|
||||
if (primaryContentValues == null) {
|
||||
Log.e(LOG_TAG, "All ContentValues given from database is empty.");
|
||||
primaryContentValues = new ContentValues();
|
||||
}
|
||||
|
||||
// N property. This order is specified by vCard spec and does not depend on countries.
|
||||
builder.append(VCARD_PROPERTY_NAME);
|
||||
if (!(VCardUtils.containsOnlyAscii(familyName) &&
|
||||
VCardUtils.containsOnlyAscii(givenName) &&
|
||||
VCardUtils.containsOnlyAscii(middleName) &&
|
||||
VCardUtils.containsOnlyAscii(prefix) &&
|
||||
VCardUtils.containsOnlyAscii(suffix))) {
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
}
|
||||
final String familyName = primaryContentValues
|
||||
.getAsString(StructuredName.FAMILY_NAME);
|
||||
final String middleName = primaryContentValues
|
||||
.getAsString(StructuredName.MIDDLE_NAME);
|
||||
final String givenName = primaryContentValues
|
||||
.getAsString(StructuredName.GIVEN_NAME);
|
||||
final String prefix = primaryContentValues
|
||||
.getAsString(StructuredName.PREFIX);
|
||||
final String suffix = primaryContentValues
|
||||
.getAsString(StructuredName.SUFFIX);
|
||||
final String displayName = primaryContentValues
|
||||
.getAsString(StructuredName.DISPLAY_NAME);
|
||||
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(encodedFamily);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedGiven);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedMiddle);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedPrefix);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedSuffix);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
// For now, some primary element is not encoded into Quoted-Printable, which is not
|
||||
// valid in vCard spec strictly. In the future, we may have to have some flag to
|
||||
// enable composer to encode these primary field into Quoted-Printable.
|
||||
if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) {
|
||||
final String encodedFamily = escapeCharacters(familyName);
|
||||
final String encodedGiven = escapeCharacters(givenName);
|
||||
final String encodedMiddle = escapeCharacters(middleName);
|
||||
final String encodedPrefix = escapeCharacters(prefix);
|
||||
final String encodedSuffix = escapeCharacters(suffix);
|
||||
|
||||
final String encodedFullname = VCardUtils.constructNameFromElements(
|
||||
VCardConfig.getNameOrderType(mVCardType),
|
||||
encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix);
|
||||
|
||||
// FN property
|
||||
builder.append(VCARD_PROPERTY_FULL_NAME);
|
||||
if (!VCardUtils.containsOnlyAscii(encodedFullname)) {
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
}
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(encodedFullname);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
} else if (!TextUtils.isEmpty(displayName)) {
|
||||
builder.append(VCARD_PROPERTY_NAME);
|
||||
// N property. This order is specified by vCard spec and does not depend on countries.
|
||||
builder.append(VCARD_PROPERTY_NAME);
|
||||
if (!(VCardUtils.containsOnlyPrintableAscii(familyName) &&
|
||||
VCardUtils.containsOnlyPrintableAscii(givenName) &&
|
||||
VCardUtils.containsOnlyPrintableAscii(middleName) &&
|
||||
VCardUtils.containsOnlyPrintableAscii(prefix) &&
|
||||
VCardUtils.containsOnlyPrintableAscii(suffix))) {
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(escapeCharacters(displayName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
} else if (mIsDoCoMo) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
|
||||
}
|
||||
|
||||
String phoneticFamilyName = contentValues
|
||||
.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
|
||||
String phoneticMiddleName = contentValues
|
||||
.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
|
||||
String phoneticGivenName = contentValues
|
||||
.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);
|
||||
}
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(encodedFamily);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedGiven);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedMiddle);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedPrefix);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(encodedSuffix);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
|
||||
if (mIsV30) {
|
||||
final String sortString = VCardUtils
|
||||
.constructNameFromElements(mVCardType,
|
||||
phoneticFamilyName, phoneticMiddleName,
|
||||
phoneticGivenName);
|
||||
builder.append(VCARD_PROPERTY_SORT_STRING);
|
||||
final String encodedFullname = VCardUtils.constructNameFromElements(
|
||||
VCardConfig.getNameOrderType(mVCardType),
|
||||
encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix);
|
||||
|
||||
if (!VCardUtils.containsOnlyAscii(sortString)) {
|
||||
// Strictly, adding charset information is NOT valid in
|
||||
// VCard 3.0,
|
||||
// but we'll add this info since parser side may be able to
|
||||
// use the charset via
|
||||
// this attribute field.
|
||||
//
|
||||
// e.g. Japanese mobile phones use Shift_Jis while RFC 2426
|
||||
// recommends
|
||||
// UTF-8. By adding this field, parsers may be able to know
|
||||
// this text
|
||||
// is NOT UTF-8 but Shift_Jis.
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
}
|
||||
// FN property
|
||||
builder.append(VCARD_PROPERTY_FULL_NAME);
|
||||
if (!VCardUtils.containsOnlyPrintableAscii(encodedFullname)) {
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
}
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(encodedFullname);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
} else if (!TextUtils.isEmpty(displayName)) {
|
||||
builder.append(VCARD_PROPERTY_NAME);
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(escapeCharacters(displayName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
} else if (mIsDoCoMo) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
|
||||
} else if (mIsV30) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
|
||||
appendVCardLine(builder, VCARD_PROPERTY_FULL_NAME, "");
|
||||
}
|
||||
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(sortString);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
} else {
|
||||
// Note: There is no appropriate property for expressing
|
||||
// phonetic name in
|
||||
// VCard 2.1, while there is in VCard 3.0 (SORT-STRING).
|
||||
// We chose to use DoCoMo's way since it is supported by a
|
||||
// lot of
|
||||
// Japanese mobile phones.
|
||||
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);
|
||||
}
|
||||
|
||||
if (mIsV30) {
|
||||
final String sortString = VCardUtils
|
||||
.constructNameFromElements(mVCardType,
|
||||
phoneticFamilyName, phoneticMiddleName,
|
||||
phoneticGivenName);
|
||||
builder.append(VCARD_PROPERTY_SORT_STRING);
|
||||
|
||||
if (!VCardUtils.containsOnlyPrintableAscii(sortString)) {
|
||||
// Strictly, adding charset information is NOT valid in
|
||||
// VCard 3.0,
|
||||
// but we'll add this info since parser side may be able to
|
||||
// use the charset via
|
||||
// this attribute field.
|
||||
//
|
||||
// TODO: should use Quoted-Pritable?
|
||||
builder.append(VCARD_PROPERTY_SOUND);
|
||||
// e.g. Japanese mobile phones use Shift_Jis while RFC 2426
|
||||
// recommends
|
||||
// UTF-8. By adding this field, parsers may be able to know
|
||||
// this text
|
||||
// is NOT UTF-8 but Shift_Jis.
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(Constants.ATTR_TYPE_X_IRMC_N);
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
|
||||
if (!(VCardUtils.containsOnlyAscii(phoneticFamilyName) &&
|
||||
VCardUtils.containsOnlyAscii(phoneticMiddleName) &&
|
||||
VCardUtils.containsOnlyAscii(phoneticGivenName))) {
|
||||
builder.append(mVCardAttributeCharset);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
}
|
||||
|
||||
builder.append(escapeCharacters(phoneticFamilyName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(escapeCharacters(phoneticMiddleName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(escapeCharacters(phoneticGivenName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
|
||||
if (mUsesAndroidProperty) {
|
||||
final String phoneticName = VCardUtils
|
||||
.constructNameFromElements(mVCardType,
|
||||
phoneticFamilyName, phoneticMiddleName,
|
||||
phoneticGivenName);
|
||||
builder.append(VCARD_PROPERTY_X_PHONETIC_NAME);
|
||||
|
||||
if (!VCardUtils.containsOnlyAscii(phoneticName)) {
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
}
|
||||
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
// TODO: may need to make the text quoted-printable.
|
||||
builder.append(phoneticName);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
builder.append(mVCardAttributeCharset);
|
||||
}
|
||||
} else if (mIsDoCoMo) {
|
||||
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(sortString);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
} else {
|
||||
// Note: There is no appropriate property for expressing
|
||||
// phonetic name in
|
||||
// VCard 2.1, while there is in VCard 3.0 (SORT-STRING).
|
||||
// We chose to use DoCoMo's way since it is supported by a
|
||||
// lot of Japanese mobile phones.
|
||||
//
|
||||
// TODO: should use Quoted-Pritable?
|
||||
builder.append(VCARD_PROPERTY_SOUND);
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(Constants.ATTR_TYPE_X_IRMC_N);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
|
||||
if (!(VCardUtils.containsOnlyPrintableAscii(phoneticFamilyName) &&
|
||||
VCardUtils.containsOnlyPrintableAscii(phoneticMiddleName) &&
|
||||
VCardUtils.containsOnlyPrintableAscii(phoneticGivenName))) {
|
||||
builder.append(mVCardAttributeCharset);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
}
|
||||
|
||||
builder.append(escapeCharacters(phoneticFamilyName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(escapeCharacters(phoneticMiddleName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(escapeCharacters(phoneticGivenName));
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
} else if (mIsDoCoMo) {
|
||||
builder.append(VCARD_PROPERTY_SOUND);
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(Constants.ATTR_TYPE_X_IRMC_N);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_ITEM_SEPARATOR);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
|
||||
if (mUsesDefactProperty) {
|
||||
if (!TextUtils.isEmpty(phoneticGivenName)) {
|
||||
builder.append(VCARD_PROPERTY_X_PHONETIC_FIRST_NAME);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(phoneticGivenName);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
if (!TextUtils.isEmpty(phoneticMiddleName)) {
|
||||
builder.append(VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(phoneticMiddleName);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
if (!TextUtils.isEmpty(phoneticFamilyName)) {
|
||||
builder.append(VCARD_PROPERTY_X_PHONETIC_LAST_NAME);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(phoneticFamilyName);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
if (mUsesDefactProperty) {
|
||||
if (!TextUtils.isEmpty(phoneticGivenName)) {
|
||||
builder.append(VCARD_PROPERTY_X_PHONETIC_FIRST_NAME);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(phoneticGivenName);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
if (!TextUtils.isEmpty(phoneticMiddleName)) {
|
||||
builder.append(VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(phoneticMiddleName);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
if (!TextUtils.isEmpty(phoneticFamilyName)) {
|
||||
builder.append(VCARD_PROPERTY_X_PHONETIC_LAST_NAME);
|
||||
builder.append(VCARD_DATA_SEPARATOR);
|
||||
builder.append(phoneticFamilyName);
|
||||
builder.append(VCARD_COL_SEPARATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -974,7 +982,7 @@ public class VCardComposer {
|
||||
}
|
||||
builder.append(propertyNickname);
|
||||
|
||||
if (!VCardUtils.containsOnlyAscii(propertyNickname)) {
|
||||
if (!VCardUtils.containsOnlyPrintableAscii(propertyNickname)) {
|
||||
// Strictly, this is not valid in vCard 3.0. See above.
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(mVCardAttributeCharset);
|
||||
@@ -991,16 +999,31 @@ public class VCardComposer {
|
||||
final Map<String, List<ContentValues>> contentValuesListMap) {
|
||||
final List<ContentValues> contentValuesList = contentValuesListMap
|
||||
.get(Phone.CONTENT_ITEM_TYPE);
|
||||
boolean phoneLineExists = false;
|
||||
if (contentValuesList != null) {
|
||||
Set<String> phoneSet = new HashSet<String>();
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
Integer phoneType = contentValues.getAsInteger(Phone.TYPE);
|
||||
int phoneTypeAsPrimitive =
|
||||
(phoneType == null ? Phone.TYPE_HOME : phoneType);
|
||||
appendVCardTelephoneLine(builder, phoneTypeAsPrimitive,
|
||||
contentValues.getAsString(Phone.LABEL),
|
||||
contentValues.getAsString(Phone.NUMBER));
|
||||
final Integer typeAsObject = contentValues.getAsInteger(Phone.TYPE);
|
||||
final String label = contentValues.getAsString(Phone.LABEL);
|
||||
String phoneNumber = contentValues.getAsString(Phone.NUMBER);
|
||||
if (phoneNumber != null) {
|
||||
phoneNumber = phoneNumber.trim();
|
||||
}
|
||||
if (TextUtils.isEmpty(phoneNumber)) {
|
||||
continue;
|
||||
}
|
||||
phoneLineExists = true;
|
||||
int type = (typeAsObject != null ? typeAsObject : Phone.TYPE_HOME);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
} else if (mIsDoCoMo) {
|
||||
}
|
||||
|
||||
if (!phoneLineExists && mIsDoCoMo) {
|
||||
appendVCardTelephoneLine(builder, Phone.TYPE_HOME, "", "");
|
||||
}
|
||||
}
|
||||
@@ -1009,14 +1032,31 @@ 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>();
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
appendVCardEmailLine(builder, contentValues
|
||||
.getAsInteger(Email.TYPE), contentValues
|
||||
.getAsString(Email.LABEL), contentValues
|
||||
.getAsString(Email.DATA));
|
||||
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();
|
||||
}
|
||||
if (TextUtils.isEmpty(emailAddress)) {
|
||||
continue;
|
||||
}
|
||||
emailAddressExists = true;
|
||||
// Do not allow completely same email address line emitted into each file.
|
||||
if (!addressSet.contains(emailAddress)) {
|
||||
addressSet.add(emailAddress);
|
||||
appendVCardEmailLine(builder, type, label, emailAddress);
|
||||
}
|
||||
}
|
||||
} else if (mIsDoCoMo) {
|
||||
}
|
||||
|
||||
if (!emailAddressExists && mIsDoCoMo) {
|
||||
appendVCardEmailLine(builder, Email.TYPE_HOME, "", "");
|
||||
}
|
||||
}
|
||||
@@ -1087,7 +1127,9 @@ public class VCardComposer {
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE);
|
||||
final String label = contentValues.getAsString(StructuredPostal.LABEL);
|
||||
appendVCardPostalLine(builder, type, label, contentValues);
|
||||
if (type != null) {
|
||||
appendVCardPostalLine(builder, type, label, contentValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,6 +1141,12 @@ public class VCardComposer {
|
||||
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)) {
|
||||
@@ -1116,8 +1164,13 @@ public class VCardComposer {
|
||||
.get(Website.CONTENT_ITEM_TYPE);
|
||||
if (contentValuesList != null) {
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
final String website = contentValues.getAsString(Website.URL);
|
||||
appendVCardLine(builder, VCARD_PROPERTY_URL, website);
|
||||
String website = contentValues.getAsString(Website.URL);
|
||||
if (website != null) {
|
||||
website = website.trim();
|
||||
}
|
||||
if (!TextUtils.isEmpty(website)) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_URL, website);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1130,8 +1183,13 @@ public class VCardComposer {
|
||||
// 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.
|
||||
final String birthday = contentValuesList.get(0).getAsString(Birthday.BIRTHDAY);
|
||||
appendVCardLine(builder, VCARD_PROPERTY_BIRTHDAY, birthday);
|
||||
String birthday = contentValuesList.get(0).getAsString(Birthday.BIRTHDAY);
|
||||
if (birthday != null) {
|
||||
birthday = birthday.trim();
|
||||
}
|
||||
if (!TextUtils.isEmpty(birthday)) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_BIRTHDAY, birthday);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,14 +1199,29 @@ public class VCardComposer {
|
||||
.get(Organization.CONTENT_ITEM_TYPE);
|
||||
if (contentValuesList != null) {
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
final String company = contentValues
|
||||
String company = contentValues
|
||||
.getAsString(Organization.COMPANY);
|
||||
final String title = contentValues
|
||||
if (company != null) {
|
||||
company = company.trim();
|
||||
}
|
||||
String title = contentValues
|
||||
.getAsString(Organization.TITLE);
|
||||
appendVCardLine(builder, VCARD_PROPERTY_ORG, company, true,
|
||||
mUsesQuotedPrintable);
|
||||
appendVCardLine(builder, VCARD_PROPERTY_TITLE, title, true,
|
||||
mUsesQuotedPrintable);
|
||||
if (title != null) {
|
||||
title = title.trim();
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(company)) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_ORG, company,
|
||||
!VCardUtils.containsOnlyPrintableAscii(company),
|
||||
(mUsesQuotedPrintable &&
|
||||
!VCardUtils.containsOnlyNonCrLfPrintableAscii(company)));
|
||||
}
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_TITLE, title,
|
||||
!VCardUtils.containsOnlyPrintableAscii(title),
|
||||
(mUsesQuotedPrintable &&
|
||||
!VCardUtils.containsOnlyNonCrLfPrintableAscii(title)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1201,7 +1274,10 @@ public class VCardComposer {
|
||||
StringBuilder noteBuilder = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
final String note = contentValues.getAsString(Note.NOTE);
|
||||
String note = contentValues.getAsString(Note.NOTE);
|
||||
if (note == null) {
|
||||
note = "";
|
||||
}
|
||||
if (note.length() > 0) {
|
||||
if (first) {
|
||||
first = false;
|
||||
@@ -1211,14 +1287,27 @@ public class VCardComposer {
|
||||
noteBuilder.append(note);
|
||||
}
|
||||
}
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteBuilder.toString(),
|
||||
true, mUsesQuotedPrintable);
|
||||
final String noteStr = noteBuilder.toString();
|
||||
// This means we scan noteStr completely twice, which is redundant.
|
||||
// But for now, we assume this is not so time-consuming..
|
||||
final boolean shouldAppendCharsetInfo =
|
||||
!VCardUtils.containsOnlyPrintableAscii(noteStr);
|
||||
final boolean reallyUseQuotedPrintable =
|
||||
(mUsesQuotedPrintable &&
|
||||
!VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr));
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteStr,
|
||||
shouldAppendCharsetInfo, reallyUseQuotedPrintable);
|
||||
} else {
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
final String note = contentValues.getAsString(Note.NOTE);
|
||||
if (!TextUtils.isEmpty(note)) {
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NOTE, note, true,
|
||||
mUsesQuotedPrintable);
|
||||
final String noteStr = contentValues.getAsString(Note.NOTE);
|
||||
if (!TextUtils.isEmpty(noteStr)) {
|
||||
final boolean shouldAppendCharsetInfo =
|
||||
!VCardUtils.containsOnlyPrintableAscii(noteStr);
|
||||
final boolean reallyUseQuotedPrintable =
|
||||
(mUsesQuotedPrintable &&
|
||||
!VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr));
|
||||
appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteStr,
|
||||
shouldAppendCharsetInfo, reallyUseQuotedPrintable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1339,15 +1428,31 @@ public class VCardComposer {
|
||||
builder.append(VCARD_PROPERTY_ADR);
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
if (mUsesQuotedPrintable && !VCardUtils.containsOnlyNonCrLfPrintableAscii(data)) {
|
||||
actuallyUseQuotedPrintable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int length = dataArray.length;
|
||||
final boolean useQuotedPrintable = mUsesQuotedPrintable;
|
||||
for (int i = 0; i < length; i++) {
|
||||
String data = dataArray[i];
|
||||
if (!TextUtils.isEmpty(data)) {
|
||||
dataExists = true;
|
||||
if (useQuotedPrintable) {
|
||||
if (actuallyUseQuotedPrintable) {
|
||||
dataArray[i] = encodeQuotedPrintable(data);
|
||||
} else {
|
||||
dataArray[i] = escapeCharacters(data);
|
||||
@@ -1394,25 +1499,40 @@ public class VCardComposer {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeAsString != null) {
|
||||
appendTypeAttribute(builder, typeAsString);
|
||||
}
|
||||
// Attribute(s).
|
||||
|
||||
if (dataExists) {
|
||||
// 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.
|
||||
{
|
||||
boolean shouldAppendAttrSeparator = false;
|
||||
if (typeAsString != null) {
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
appendTypeAttribute(builder, typeAsString);
|
||||
shouldAppendAttrSeparator = true;
|
||||
}
|
||||
builder.append(mVCardAttributeCharset);
|
||||
|
||||
if (useQuotedPrintable) {
|
||||
builder.append(VCARD_ATTR_SEPARATOR);
|
||||
builder.append(VCARD_ATTR_ENCODING_QP);
|
||||
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 (actuallyUseQuotedPrintable) {
|
||||
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
|
||||
|
||||
@@ -283,7 +283,7 @@ public class VCardUtils {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static boolean containsOnlyAscii(String str) {
|
||||
public static boolean containsOnlyPrintableAscii(String str) {
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
return true;
|
||||
}
|
||||
@@ -299,13 +299,35 @@ public class VCardUtils {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is useful when checking the string should be encoded into quoted-printable
|
||||
* or not, which is required by vCard 2.1.
|
||||
* See the definition of "7bit" in vCard 2.1 spec for more information.
|
||||
*/
|
||||
public static boolean containsOnlyNonCrLfPrintableAscii(String str) {
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final int length = str.length();
|
||||
final int asciiFirst = 0x20;
|
||||
final int asciiLast = 0x126;
|
||||
for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
|
||||
int c = str.codePointAt(i);
|
||||
if (c < asciiFirst || asciiLast < c || c == '\n' || c == '\r') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful since vCard 3.0 often requires the ("X-") properties and groups
|
||||
* should contain only alphabets, digits, and hyphen.
|
||||
*
|
||||
* Note: It is already known some devices (wrongly) outputs properties with characters
|
||||
* which should not be in the field. One example is "X-GOOGLE TALK". We appreciate
|
||||
* which should not be in the field. One example is "X-GOOGLE TALK". We accept
|
||||
* such kind of input but must never output it unless the target is very specific
|
||||
* to the device which is able to parse the malformed input.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user