am b1f5a54a: Merge "VCard refactoring backport." into gingerbread
Merge commit 'b1f5a54a2abd95d03dc96f5f691495d2d7dfadc7' into gingerbread-plus-aosp * commit 'b1f5a54a2abd95d03dc96f5f691495d2d7dfadc7': VCard refactoring backport.
This commit is contained in:
@@ -27,7 +27,6 @@ import java.util.Map;
|
||||
new HashMap<Character, String>();
|
||||
|
||||
static {
|
||||
// There's no logical mapping rule in Unicode. Sigh.
|
||||
sHalfWidthMap.put('\u3001', "\uFF64");
|
||||
sHalfWidthMap.put('\u3002', "\uFF61");
|
||||
sHalfWidthMap.put('\u300C', "\uFF62");
|
||||
@@ -366,11 +365,11 @@ import java.util.Map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return half-width version of that character if possible. Return null if not possible
|
||||
* Returns half-width version of that character if possible. Returns null if not possible
|
||||
* @param ch input character
|
||||
* @return CharSequence object if the mapping for ch exists. Return null otherwise.
|
||||
*/
|
||||
public static String tryGetHalfWidthText(char ch) {
|
||||
public static String tryGetHalfWidthText(final char ch) {
|
||||
if (sHalfWidthMap.containsKey(ch)) {
|
||||
return sHalfWidthMap.get(ch);
|
||||
} else {
|
||||
|
||||
@@ -30,11 +30,10 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.ArrayList;
|
||||
@@ -47,7 +46,23 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The class which lets users create their own vCard String.
|
||||
* <p>
|
||||
* The class which lets users create their own vCard String. Typical usage is as follows:
|
||||
* </p>
|
||||
* <pre class="prettyprint">final VCardBuilder builder = new VCardBuilder(vcardType);
|
||||
* builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
|
||||
* .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
|
||||
* .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
|
||||
* .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
|
||||
* .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
|
||||
* .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
|
||||
* .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE))
|
||||
* .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE))
|
||||
* .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
|
||||
* .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
|
||||
* .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
|
||||
* .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
|
||||
* return builder.toString();</pre>
|
||||
*/
|
||||
public class VCardBuilder {
|
||||
private static final String LOG_TAG = "VCardBuilder";
|
||||
@@ -75,81 +90,129 @@ public class VCardBuilder {
|
||||
private static final String VCARD_WS = " ";
|
||||
private static final String VCARD_PARAM_EQUAL = "=";
|
||||
|
||||
private static final String VCARD_PARAM_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE";
|
||||
|
||||
private static final String VCARD_PARAM_ENCODING_BASE64_V21 = "ENCODING=BASE64";
|
||||
private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b";
|
||||
private static final String VCARD_PARAM_ENCODING_QP =
|
||||
"ENCODING=" + VCardConstants.PARAM_ENCODING_QP;
|
||||
private static final String VCARD_PARAM_ENCODING_BASE64_V21 =
|
||||
"ENCODING=" + VCardConstants.PARAM_ENCODING_BASE64;
|
||||
private static final String VCARD_PARAM_ENCODING_BASE64_AS_B =
|
||||
"ENCODING=" + VCardConstants.PARAM_ENCODING_B;
|
||||
|
||||
private static final String SHIFT_JIS = "SHIFT_JIS";
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
|
||||
private final int mVCardType;
|
||||
|
||||
private final boolean mIsV30;
|
||||
private final boolean mIsV30OrV40;
|
||||
private final boolean mIsJapaneseMobilePhone;
|
||||
private final boolean mOnlyOneNoteFieldIsAvailable;
|
||||
private final boolean mIsDoCoMo;
|
||||
private final boolean mShouldUseQuotedPrintable;
|
||||
private final boolean mUsesAndroidProperty;
|
||||
private final boolean mUsesDefactProperty;
|
||||
private final boolean mUsesUtf8;
|
||||
private final boolean mUsesShiftJis;
|
||||
private final boolean mAppendTypeParamName;
|
||||
private final boolean mRefrainsQPToNameProperties;
|
||||
private final boolean mNeedsToConvertPhoneticString;
|
||||
|
||||
private final boolean mShouldAppendCharsetParam;
|
||||
|
||||
private final String mCharsetString;
|
||||
private final String mCharset;
|
||||
private final String mVCardCharsetParameter;
|
||||
|
||||
private StringBuilder mBuilder;
|
||||
private boolean mEndAppended;
|
||||
|
||||
public VCardBuilder(final int vcardType) {
|
||||
// Default charset should be used
|
||||
this(vcardType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param vcardType
|
||||
* @param charset If null, we use default charset for export.
|
||||
* @hide
|
||||
*/
|
||||
public VCardBuilder(final int vcardType, String charset) {
|
||||
mVCardType = vcardType;
|
||||
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
Log.w(LOG_TAG,
|
||||
"Should not use vCard 4.0 when building vCard. " +
|
||||
"It is not officially published yet.");
|
||||
|
||||
mIsV30OrV40 = VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType);
|
||||
mShouldUseQuotedPrintable = VCardConfig.shouldUseQuotedPrintable(vcardType);
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
mIsJapaneseMobilePhone = VCardConfig.needsToConvertPhoneticString(vcardType);
|
||||
mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType);
|
||||
mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType);
|
||||
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
|
||||
mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
|
||||
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
|
||||
mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType);
|
||||
mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
|
||||
mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType);
|
||||
|
||||
mShouldAppendCharsetParam = !(mIsV30 && mUsesUtf8);
|
||||
// vCard 2.1 requires charset.
|
||||
// vCard 3.0 does not allow it but we found some devices use it to determine
|
||||
// the exact charset.
|
||||
// We currently append it only when charset other than UTF_8 is used.
|
||||
mShouldAppendCharsetParam =
|
||||
!(VCardConfig.isVersion30(vcardType) && "UTF-8".equalsIgnoreCase(charset));
|
||||
|
||||
if (mIsDoCoMo) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
if (VCardConfig.isDoCoMo(vcardType)) {
|
||||
if (!SHIFT_JIS.equalsIgnoreCase(charset)) {
|
||||
Log.w(LOG_TAG,
|
||||
"The charset \"" + charset + "\" is used while "
|
||||
+ SHIFT_JIS + " is needed to be used.");
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = SHIFT_JIS;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
} else {
|
||||
if (mIsDoCoMo) {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"DoCoMo-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"Career-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
mCharsetString = charset;
|
||||
// Do not use mCharsetString bellow since it is different from "SHIFT_JIS" but
|
||||
// may be "DOCOMO_SHIFT_JIS" or something like that (internal expression used in
|
||||
// Android, not shown to the public).
|
||||
mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
|
||||
} else if (mUsesShiftJis) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
mCharsetString = charset;
|
||||
mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
|
||||
} else {
|
||||
mCharsetString = UTF_8;
|
||||
mVCardCharsetParameter = "CHARSET=" + UTF_8;
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
Log.i(LOG_TAG,
|
||||
"Use the charset \"" + VCardConfig.DEFAULT_EXPORT_CHARSET
|
||||
+ "\" for export.");
|
||||
mCharset = VCardConfig.DEFAULT_EXPORT_CHARSET;
|
||||
mVCardCharsetParameter = "CHARSET=" + VCardConfig.DEFAULT_EXPORT_CHARSET;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
mVCardCharsetParameter = "CHARSET=" + charset;
|
||||
}
|
||||
}
|
||||
clear();
|
||||
}
|
||||
@@ -158,9 +221,14 @@ public class VCardBuilder {
|
||||
mBuilder = new StringBuilder();
|
||||
mEndAppended = false;
|
||||
appendLine(VCardConstants.PROPERTY_BEGIN, VCARD_DATA_VCARD);
|
||||
if (mIsV30) {
|
||||
if (VCardConfig.isVersion40(mVCardType)) {
|
||||
appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V40);
|
||||
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||
appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V30);
|
||||
} else {
|
||||
if (!VCardConfig.isVersion21(mVCardType)) {
|
||||
Log.w(LOG_TAG, "Unknown vCard version detected.");
|
||||
}
|
||||
appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V21);
|
||||
}
|
||||
}
|
||||
@@ -226,19 +294,128 @@ public class VCardBuilder {
|
||||
return primaryContentValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* To avoid unnecessary complication in logic, we use this method to construct N, FN
|
||||
* properties for vCard 4.0.
|
||||
*/
|
||||
private VCardBuilder appendNamePropertiesV40(final List<ContentValues> contentValuesList) {
|
||||
if (mIsDoCoMo || mNeedsToConvertPhoneticString) {
|
||||
// Ignore all flags that look stale from the view of vCard 4.0 to
|
||||
// simplify construction algorithm. Actually we don't have any vCard file
|
||||
// available from real world yet, so we may need to re-enable some of these
|
||||
// in the future.
|
||||
Log.w(LOG_TAG, "Invalid flag is used in vCard 4.0 construction. Ignored.");
|
||||
}
|
||||
|
||||
if (contentValuesList == null || contentValuesList.isEmpty()) {
|
||||
appendLine(VCardConstants.PROPERTY_FN, "");
|
||||
return this;
|
||||
}
|
||||
|
||||
// We have difficulty here. How can we appropriately handle StructuredName with
|
||||
// missing parts necessary for displaying while it has suppremental information.
|
||||
//
|
||||
// e.g. How to handle non-empty phonetic names with empty structured names?
|
||||
|
||||
final ContentValues contentValues = getPrimaryContentValue(contentValuesList);
|
||||
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 formattedName = contentValues.getAsString(StructuredName.DISPLAY_NAME);
|
||||
if (TextUtils.isEmpty(familyName)
|
||||
&& TextUtils.isEmpty(givenName)
|
||||
&& TextUtils.isEmpty(middleName)
|
||||
&& TextUtils.isEmpty(prefix)
|
||||
&& TextUtils.isEmpty(suffix)) {
|
||||
if (TextUtils.isEmpty(formattedName)) {
|
||||
appendLine(VCardConstants.PROPERTY_FN, "");
|
||||
return this;
|
||||
}
|
||||
familyName = formattedName;
|
||||
}
|
||||
|
||||
final String phoneticFamilyName =
|
||||
contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
|
||||
final String phoneticMiddleName =
|
||||
contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
|
||||
final String phoneticGivenName =
|
||||
contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
|
||||
final String escapedFamily = escapeCharacters(familyName);
|
||||
final String escapedGiven = escapeCharacters(givenName);
|
||||
final String escapedMiddle = escapeCharacters(middleName);
|
||||
final String escapedPrefix = escapeCharacters(prefix);
|
||||
final String escapedSuffix = escapeCharacters(suffix);
|
||||
|
||||
mBuilder.append(VCardConstants.PROPERTY_N);
|
||||
|
||||
if (!(TextUtils.isEmpty(phoneticFamilyName) &&
|
||||
TextUtils.isEmpty(phoneticMiddleName) &&
|
||||
TextUtils.isEmpty(phoneticGivenName))) {
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
final String sortAs = escapeCharacters(phoneticFamilyName)
|
||||
+ ';' + escapeCharacters(phoneticGivenName)
|
||||
+ ';' + escapeCharacters(phoneticMiddleName);
|
||||
mBuilder.append("SORT-AS=").append(
|
||||
VCardUtils.toStringAsV40ParamValue(sortAs));
|
||||
}
|
||||
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(escapedFamily);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(escapedGiven);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(escapedMiddle);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(escapedPrefix);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(escapedSuffix);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
|
||||
if (TextUtils.isEmpty(formattedName)) {
|
||||
// Note:
|
||||
// DISPLAY_NAME doesn't exist while some other elements do, which is usually
|
||||
// weird in Android, as DISPLAY_NAME should (usually) be constructed
|
||||
// from the others using locale information and its code points.
|
||||
Log.w(LOG_TAG, "DISPLAY_NAME is empty.");
|
||||
|
||||
final String escaped = escapeCharacters(VCardUtils.constructNameFromElements(
|
||||
VCardConfig.getNameOrderType(mVCardType),
|
||||
familyName, middleName, givenName, prefix, suffix));
|
||||
appendLine(VCardConstants.PROPERTY_FN, escaped);
|
||||
} else {
|
||||
final String escapedFormatted = escapeCharacters(formattedName);
|
||||
mBuilder.append(VCardConstants.PROPERTY_FN);
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(escapedFormatted);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
|
||||
// We may need X- properties for phonetic names.
|
||||
appendPhoneticNameFields(contentValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public VCardBuilder appendNameProperties(final List<ContentValues> contentValuesList) {
|
||||
if (VCardConfig.isVersion40(mVCardType)) {
|
||||
return appendNamePropertiesV40(contentValuesList);
|
||||
}
|
||||
|
||||
if (contentValuesList == null || contentValuesList.isEmpty()) {
|
||||
if (mIsDoCoMo) {
|
||||
appendLine(VCardConstants.PROPERTY_N, "");
|
||||
} else if (mIsV30) {
|
||||
if (VCardConfig.isVersion30(mVCardType)) {
|
||||
// vCard 3.0 requires "N" and "FN" properties.
|
||||
// vCard 4.0 does NOT require N, but we take care of possible backward
|
||||
// compatibility issues.
|
||||
appendLine(VCardConstants.PROPERTY_N, "");
|
||||
appendLine(VCardConstants.PROPERTY_FN, "");
|
||||
} else if (mIsDoCoMo) {
|
||||
appendLine(VCardConstants.PROPERTY_N, "");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -360,6 +537,7 @@ public class VCardBuilder {
|
||||
encodeQuotedPrintable(displayName) :
|
||||
escapeCharacters(displayName);
|
||||
|
||||
// N
|
||||
mBuilder.append(VCardConstants.PROPERTY_N);
|
||||
if (shouldAppendCharsetParam(displayName)) {
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
@@ -376,11 +554,13 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
|
||||
// FN
|
||||
mBuilder.append(VCardConstants.PROPERTY_FN);
|
||||
|
||||
// Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it
|
||||
// when it would be useful for external importers, assuming no external
|
||||
// importer allows this vioration.
|
||||
// when it would be useful or necessary for external importers,
|
||||
// assuming the external importer allows this vioration of the spec.
|
||||
if (shouldAppendCharsetParam(displayName)) {
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
mBuilder.append(mVCardCharsetParameter);
|
||||
@@ -388,8 +568,7 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(encodedDisplayName);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
} else if (mIsV30) {
|
||||
// vCard 3.0 specification requires these fields.
|
||||
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||
appendLine(VCardConstants.PROPERTY_N, "");
|
||||
appendLine(VCardConstants.PROPERTY_FN, "");
|
||||
} else if (mIsDoCoMo) {
|
||||
@@ -400,6 +579,9 @@ public class VCardBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits SOUND;IRMC, SORT-STRING, and de-fact values for phonetic names like X-PHONETIC-FAMILY.
|
||||
*/
|
||||
private void appendPhoneticNameFields(final ContentValues contentValues) {
|
||||
final String phoneticFamilyName;
|
||||
final String phoneticMiddleName;
|
||||
@@ -439,13 +621,18 @@ public class VCardBuilder {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to emit the field(s) related to phonetic name.
|
||||
if (mIsV30) {
|
||||
final String sortString = VCardUtils
|
||||
.constructNameFromElements(mVCardType,
|
||||
if (VCardConfig.isVersion40(mVCardType)) {
|
||||
// We don't want SORT-STRING anyway.
|
||||
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||
final String sortString =
|
||||
VCardUtils.constructNameFromElements(mVCardType,
|
||||
phoneticFamilyName, phoneticMiddleName, phoneticGivenName);
|
||||
mBuilder.append(VCardConstants.PROPERTY_SORT_STRING);
|
||||
if (shouldAppendCharsetParam(sortString)) {
|
||||
if (VCardConfig.isVersion30(mVCardType) && shouldAppendCharsetParam(sortString)) {
|
||||
// vCard 3.0 does not force us to use UTF-8 and actually we see some
|
||||
// programs which emit this value. It is incorrect from the view of
|
||||
// specification, but actually necessary for parsing vCard with non-UTF-8
|
||||
// charsets, expecting other parsers not get confused with this value.
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
mBuilder.append(mVCardCharsetParameter);
|
||||
}
|
||||
@@ -454,18 +641,18 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
} else if (mIsJapaneseMobilePhone) {
|
||||
// Note: There is no appropriate property for expressing
|
||||
// phonetic name in vCard 2.1, while there is in
|
||||
// phonetic name (Yomigana in Japanese) in vCard 2.1, while there is in
|
||||
// vCard 3.0 (SORT-STRING).
|
||||
// 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.
|
||||
// We use DoCoMo's way when the device is Japanese one since it is already
|
||||
// supported by a lot of Japanese mobile phones.
|
||||
// This is "X-" property, so any parser hopefully would not get
|
||||
// confused with this.
|
||||
//
|
||||
// Also, DoCoMo's specification requires vCard composer to use just the first
|
||||
// column.
|
||||
// i.e.
|
||||
// o SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
|
||||
// x SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
|
||||
// good: SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
|
||||
// bad : SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
|
||||
mBuilder.append(VCardConstants.PROPERTY_SOUND);
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N);
|
||||
@@ -519,13 +706,14 @@ public class VCardBuilder {
|
||||
mBuilder.append(encodedPhoneticGivenName);
|
||||
}
|
||||
}
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // family;given
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // given;middle
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // middle;prefix
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // prefix;suffix
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
|
||||
Log.d("@@@", "hoge");
|
||||
if (mUsesDefactProperty) {
|
||||
if (!TextUtils.isEmpty(phoneticGivenName)) {
|
||||
final boolean reallyUseQuotedPrintable =
|
||||
@@ -549,7 +737,7 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(encodedPhoneticGivenName);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
} // if (!TextUtils.isEmpty(phoneticGivenName))
|
||||
if (!TextUtils.isEmpty(phoneticMiddleName)) {
|
||||
final boolean reallyUseQuotedPrintable =
|
||||
(mShouldUseQuotedPrintable &&
|
||||
@@ -572,7 +760,7 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(encodedPhoneticMiddleName);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
} // if (!TextUtils.isEmpty(phoneticGivenName))
|
||||
if (!TextUtils.isEmpty(phoneticFamilyName)) {
|
||||
final boolean reallyUseQuotedPrintable =
|
||||
(mShouldUseQuotedPrintable &&
|
||||
@@ -595,13 +783,13 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(encodedPhoneticFamilyName);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
} // if (!TextUtils.isEmpty(phoneticFamilyName))
|
||||
}
|
||||
}
|
||||
|
||||
public VCardBuilder appendNickNames(final List<ContentValues> contentValuesList) {
|
||||
final boolean useAndroidProperty;
|
||||
if (mIsV30) {
|
||||
if (mIsV30OrV40) { // These specifications have NICKNAME property.
|
||||
useAndroidProperty = false;
|
||||
} else if (mUsesAndroidProperty) {
|
||||
useAndroidProperty = true;
|
||||
@@ -922,21 +1110,21 @@ public class VCardBuilder {
|
||||
encodedCountry = escapeCharacters(rawCountry);
|
||||
encodedNeighborhood = escapeCharacters(rawNeighborhood);
|
||||
}
|
||||
final StringBuffer addressBuffer = new StringBuffer();
|
||||
addressBuffer.append(encodedPoBox);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedStreet);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedLocality);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedRegion);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedPostalCode);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedCountry);
|
||||
final StringBuilder addressBuilder = new StringBuilder();
|
||||
addressBuilder.append(encodedPoBox);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street
|
||||
addressBuilder.append(encodedStreet);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality
|
||||
addressBuilder.append(encodedLocality);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region
|
||||
addressBuilder.append(encodedRegion);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code
|
||||
addressBuilder.append(encodedPostalCode);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country
|
||||
addressBuilder.append(encodedCountry);
|
||||
return new PostalStruct(
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuffer.toString());
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
|
||||
} else { // VCardUtils.areAllEmpty(rawAddressArray) == true
|
||||
// Try to use FORMATTED_ADDRESS instead.
|
||||
final String rawFormattedAddress =
|
||||
@@ -959,16 +1147,16 @@ public class VCardBuilder {
|
||||
// We use the second value ("Extended Address") just because Japanese mobile phones
|
||||
// do so. If the other importer expects the value be in the other field, some flag may
|
||||
// be needed.
|
||||
final StringBuffer addressBuffer = new StringBuffer();
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedFormattedAddress);
|
||||
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);
|
||||
final StringBuilder addressBuilder = new StringBuilder();
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address
|
||||
addressBuilder.append(encodedFormattedAddress);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country
|
||||
return new PostalStruct(
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuffer.toString());
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1108,7 +1296,8 @@ public class VCardBuilder {
|
||||
Log.d(LOG_TAG, "Unknown photo type. Ignored.");
|
||||
continue;
|
||||
}
|
||||
final String photoString = new String(Base64.encodeBase64(data));
|
||||
// TODO: check this works fine.
|
||||
final String photoString = new String(Base64.encode(data, Base64.NO_WRAP));
|
||||
if (!TextUtils.isEmpty(photoString)) {
|
||||
appendPhotoLine(photoString, photoType);
|
||||
}
|
||||
@@ -1165,6 +1354,8 @@ public class VCardBuilder {
|
||||
}
|
||||
|
||||
public VCardBuilder appendEvents(final List<ContentValues> contentValuesList) {
|
||||
// There's possibility where a given object may have more than one birthday, which
|
||||
// is inappropriate. We just build one birthday.
|
||||
if (contentValuesList != null) {
|
||||
String primaryBirthday = null;
|
||||
String secondaryBirthday = null;
|
||||
@@ -1232,16 +1423,19 @@ public class VCardBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param emitEveryTime If true, builder builds the line even when there's no entry.
|
||||
*/
|
||||
public void appendPostalLine(final int type, final String label,
|
||||
final ContentValues contentValues,
|
||||
final boolean isPrimary, final boolean emitLineEveryTime) {
|
||||
final boolean isPrimary, final boolean emitEveryTime) {
|
||||
final boolean reallyUseQuotedPrintable;
|
||||
final boolean appendCharset;
|
||||
final String addressValue;
|
||||
{
|
||||
PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
|
||||
if (postalStruct == null) {
|
||||
if (emitLineEveryTime) {
|
||||
if (emitEveryTime) {
|
||||
reallyUseQuotedPrintable = false;
|
||||
appendCharset = false;
|
||||
addressValue = "";
|
||||
@@ -1463,7 +1657,7 @@ public class VCardBuilder {
|
||||
parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
|
||||
} else if (VCardUtils.isMobilePhoneLabel(label)) {
|
||||
parameterList.add(VCardConstants.PARAM_TYPE_CELL);
|
||||
} else if (mIsV30) {
|
||||
} else if (mIsV30OrV40) {
|
||||
// This label is appropriately encoded in appendTypeParameters.
|
||||
parameterList.add(label);
|
||||
} else {
|
||||
@@ -1526,8 +1720,8 @@ public class VCardBuilder {
|
||||
StringBuilder tmpBuilder = new StringBuilder();
|
||||
tmpBuilder.append(VCardConstants.PROPERTY_PHOTO);
|
||||
tmpBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
if (mIsV30) {
|
||||
tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V30);
|
||||
if (mIsV30OrV40) {
|
||||
tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_AS_B);
|
||||
} else {
|
||||
tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V21);
|
||||
}
|
||||
@@ -1559,7 +1753,8 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
|
||||
public void appendAndroidSpecificProperty(final String mimeType, ContentValues contentValues) {
|
||||
public void appendAndroidSpecificProperty(
|
||||
final String mimeType, ContentValues contentValues) {
|
||||
if (!sAllowedAndroidPropertySet.contains(mimeType)) {
|
||||
return;
|
||||
}
|
||||
@@ -1681,7 +1876,7 @@ public class VCardBuilder {
|
||||
encodedValue = encodeQuotedPrintable(rawValue);
|
||||
} else {
|
||||
// TODO: one line may be too huge, which may be invalid in vCard spec, though
|
||||
// several (even well-known) applications do not care this.
|
||||
// several (even well-known) applications do not care that violation.
|
||||
encodedValue = escapeCharacters(rawValue);
|
||||
}
|
||||
|
||||
@@ -1744,7 +1939,12 @@ public class VCardBuilder {
|
||||
// which would be recommended way in vcard 3.0 though not valid in vCard 2.1.
|
||||
boolean first = true;
|
||||
for (final String typeValue : types) {
|
||||
if (VCardConfig.isV30(mVCardType)) {
|
||||
if (VCardConfig.isVersion30(mVCardType)) {
|
||||
final String encoded = VCardUtils.toStringAsV30ParamValue(typeValue);
|
||||
if (TextUtils.isEmpty(encoded)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: vCard 3.0 specifies the different type of acceptable type Strings, but
|
||||
// we don't emit that kind of vCard 3.0 specific type since there should be
|
||||
// high probabilyty in which external importers cannot understand them.
|
||||
@@ -1756,7 +1956,7 @@ public class VCardBuilder {
|
||||
} else {
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
}
|
||||
appendTypeParameter(VCardUtils.toStringAvailableAsV30ParameValue(typeValue));
|
||||
appendTypeParameter(encoded);
|
||||
} else { // vCard 2.1
|
||||
if (!VCardUtils.isV21Word(typeValue)) {
|
||||
continue;
|
||||
@@ -1783,7 +1983,8 @@ public class VCardBuilder {
|
||||
// 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) && !mIsDoCoMo) {
|
||||
if (VCardConfig.isVersion40(mVCardType) ||
|
||||
((VCardConfig.isVersion30(mVCardType) || mAppendTypeParamName) && !mIsDoCoMo)) {
|
||||
builder.append(VCardConstants.PARAM_TYPE).append(VCARD_PARAM_EQUAL);
|
||||
}
|
||||
builder.append(type);
|
||||
@@ -1825,9 +2026,9 @@ public class VCardBuilder {
|
||||
byte[] strArray = null;
|
||||
|
||||
try {
|
||||
strArray = str.getBytes(mCharsetString);
|
||||
strArray = str.getBytes(mCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Charset " + mCharsetString + " cannot be used. "
|
||||
Log.e(LOG_TAG, "Charset " + mCharset + " cannot be used. "
|
||||
+ "Try default charset");
|
||||
strArray = str.getBytes();
|
||||
}
|
||||
@@ -1893,7 +2094,7 @@ public class VCardBuilder {
|
||||
break;
|
||||
}
|
||||
case '\\': {
|
||||
if (mIsV30) {
|
||||
if (mIsV30OrV40) {
|
||||
tmpBuilder.append("\\\\");
|
||||
break;
|
||||
} else {
|
||||
@@ -1911,7 +2112,7 @@ public class VCardBuilder {
|
||||
break;
|
||||
}
|
||||
case ',': {
|
||||
if (mIsV30) {
|
||||
if (mIsV30OrV40) {
|
||||
tmpBuilder.append("\\,");
|
||||
} else {
|
||||
tmpBuilder.append(ch);
|
||||
|
||||
@@ -19,16 +19,12 @@ import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Entity;
|
||||
import android.content.EntityIterator;
|
||||
import android.content.Entity.NamedContentValues;
|
||||
import android.content.EntityIterator;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
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.RawContactsEntity;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
||||
@@ -41,6 +37,11 @@ import android.provider.ContactsContract.CommonDataKinds.Relation;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.provider.ContactsContract.RawContactsEntity;
|
||||
import android.text.TextUtils;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -61,15 +62,11 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class for composing VCard from Contacts information. Note that this is
|
||||
* completely differnt implementation from
|
||||
* android.syncml.pim.vcard.VCardComposer, which is not maintained anymore.
|
||||
* The class for composing vCard from Contacts information.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Usually, this class should be used like this.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="prettyprint">VCardComposer composer = null;
|
||||
* try {
|
||||
* composer = new VCardComposer(context);
|
||||
@@ -93,15 +90,18 @@ import java.util.Map;
|
||||
* if (composer != null) {
|
||||
* composer.terminate();
|
||||
* }
|
||||
* } </pre>
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Users have to manually take care of memory efficiency. Even one vCard may contain
|
||||
* image of non-trivial size for mobile devices.
|
||||
* </p>
|
||||
* <p>
|
||||
* {@link VCardBuilder} is used to build each vCard.
|
||||
* </p>
|
||||
*/
|
||||
public class VCardComposer {
|
||||
private static final String LOG_TAG = "VCardComposer";
|
||||
|
||||
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";
|
||||
|
||||
@@ -119,6 +119,8 @@ public class VCardComposer {
|
||||
|
||||
public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
|
||||
|
||||
// Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here,
|
||||
// since usual vCard devices for Japanese devices already use it.
|
||||
private static final String SHIFT_JIS = "SHIFT_JIS";
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
|
||||
@@ -141,7 +143,7 @@ public class VCardComposer {
|
||||
sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
|
||||
sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
|
||||
sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
|
||||
// Google talk is a special case.
|
||||
// We don't add Google talk here since it has to be handled separately.
|
||||
}
|
||||
|
||||
public static interface OneEntryHandler {
|
||||
@@ -152,37 +154,37 @@ public class VCardComposer {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An useful example handler, which emits VCard String to outputstream one by one.
|
||||
* An useful handler for emitting vCard String to an OutputStream object one by one.
|
||||
* </p>
|
||||
* <p>
|
||||
* The input OutputStream object is closed() on {@link #onTerminate()}.
|
||||
* Must not close the stream outside.
|
||||
* Must not close the stream outside this class.
|
||||
* </p>
|
||||
*/
|
||||
public class HandlerForOutputStream implements OneEntryHandler {
|
||||
public final class HandlerForOutputStream implements OneEntryHandler {
|
||||
@SuppressWarnings("hiding")
|
||||
private static final String LOG_TAG = "vcard.VCardComposer.HandlerForOutputStream";
|
||||
|
||||
final private OutputStream mOutputStream; // mWriter will close this.
|
||||
private Writer mWriter;
|
||||
private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream";
|
||||
|
||||
private boolean mOnTerminateIsCalled = false;
|
||||
|
||||
private final OutputStream mOutputStream; // mWriter will close this.
|
||||
private Writer mWriter;
|
||||
|
||||
/**
|
||||
* Input stream will be closed on the detruction of this object.
|
||||
*/
|
||||
public HandlerForOutputStream(OutputStream outputStream) {
|
||||
public HandlerForOutputStream(final OutputStream outputStream) {
|
||||
mOutputStream = outputStream;
|
||||
}
|
||||
|
||||
public boolean onInit(Context context) {
|
||||
public boolean onInit(final Context context) {
|
||||
try {
|
||||
mWriter = new BufferedWriter(new OutputStreamWriter(
|
||||
mOutputStream, mCharsetString));
|
||||
mOutputStream, mCharset));
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.e(LOG_TAG, "Unsupported charset: " + mCharsetString);
|
||||
Log.e(LOG_TAG, "Unsupported charset: " + mCharset);
|
||||
mErrorReason = "Encoding is not supported (usually this does not happen!): "
|
||||
+ mCharsetString;
|
||||
+ mCharset;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -235,14 +237,19 @@ public class VCardComposer {
|
||||
"IOException during closing the output stream: "
|
||||
+ e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
mWriter.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
closeOutputStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void closeOutputStream() {
|
||||
try {
|
||||
mWriter.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize() {
|
||||
if (!mOnTerminateIsCalled) {
|
||||
@@ -257,11 +264,10 @@ public class VCardComposer {
|
||||
private final ContentResolver mContentResolver;
|
||||
|
||||
private final boolean mIsDoCoMo;
|
||||
private final boolean mUsesShiftJis;
|
||||
private Cursor mCursor;
|
||||
private int mIdColumn;
|
||||
|
||||
private final String mCharsetString;
|
||||
private final String mCharset;
|
||||
private boolean mTerminateIsCalled;
|
||||
private final List<OneEntryHandler> mHandlerList;
|
||||
|
||||
@@ -272,21 +278,39 @@ public class VCardComposer {
|
||||
};
|
||||
|
||||
public VCardComposer(Context context) {
|
||||
this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
|
||||
this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The variant which sets charset to null and sets careHandlerErrors to true.
|
||||
*/
|
||||
public VCardComposer(Context context, int vcardType) {
|
||||
this(context, vcardType, true);
|
||||
this(context, vcardType, null, true);
|
||||
}
|
||||
|
||||
public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) {
|
||||
this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors);
|
||||
public VCardComposer(Context context, int vcardType, String charset) {
|
||||
this(context, vcardType, charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The variant which sets charset to null.
|
||||
*/
|
||||
public VCardComposer(final Context context, final int vcardType,
|
||||
final boolean careHandlerErrors) {
|
||||
this(context, vcardType, null, careHandlerErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for supporting call log entry vCard composing.
|
||||
*
|
||||
* @param context Context to be used during the composition.
|
||||
* @param vcardType The type of vCard, typically available via {@link VCardConfig}.
|
||||
* @param charset The charset to be used. Use null when you don't need the charset.
|
||||
* @param careHandlerErrors If true, This object returns false everytime
|
||||
* a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false.
|
||||
* If false, this ignores those errors.
|
||||
*/
|
||||
public VCardComposer(final Context context, final int vcardType,
|
||||
public VCardComposer(final Context context, final int vcardType, String charset,
|
||||
final boolean careHandlerErrors) {
|
||||
mContext = context;
|
||||
mVCardType = vcardType;
|
||||
@@ -294,30 +318,67 @@ public class VCardComposer {
|
||||
mContentResolver = context.getContentResolver();
|
||||
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
|
||||
mHandlerList = new ArrayList<OneEntryHandler>();
|
||||
|
||||
if (mIsDoCoMo) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset);
|
||||
final boolean shouldAppendCharsetParam = !(
|
||||
VCardConfig.isVersion30(vcardType) && UTF_8.equalsIgnoreCase(charset));
|
||||
|
||||
if (mIsDoCoMo || shouldAppendCharsetParam) {
|
||||
if (SHIFT_JIS.equalsIgnoreCase(charset)) {
|
||||
if (mIsDoCoMo) {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"DoCoMo-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"Career-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
}
|
||||
mCharset = charset;
|
||||
} else {
|
||||
Log.w(LOG_TAG,
|
||||
"The charset \"" + charset + "\" is used while "
|
||||
+ SHIFT_JIS + " is needed to be used.");
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = SHIFT_JIS;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
}
|
||||
mCharsetString = charset;
|
||||
} else if (mUsesShiftJis) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
mCharsetString = charset;
|
||||
} else {
|
||||
mCharsetString = UTF_8;
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = UTF_8;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "Use the charset \"" + mCharset + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +412,7 @@ public class VCardComposer {
|
||||
}
|
||||
|
||||
if (mCareHandlerErrors) {
|
||||
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||
final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||
mHandlerList.size());
|
||||
for (OneEntryHandler handler : mHandlerList) {
|
||||
if (!handler.onInit(mContext)) {
|
||||
@@ -414,7 +475,7 @@ public class VCardComposer {
|
||||
mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
|
||||
return false;
|
||||
}
|
||||
String vcard;
|
||||
final String vcard;
|
||||
try {
|
||||
if (mIdColumn >= 0) {
|
||||
vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
|
||||
@@ -437,8 +498,7 @@ public class VCardComposer {
|
||||
mCursor.moveToNext();
|
||||
}
|
||||
|
||||
// This function does not care the OutOfMemoryError on the handler side
|
||||
// :-P
|
||||
// This function does not care the OutOfMemoryError on the handler side :-P
|
||||
if (mCareHandlerErrors) {
|
||||
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||
mHandlerList.size());
|
||||
@@ -457,7 +517,7 @@ public class VCardComposer {
|
||||
}
|
||||
|
||||
private String createOneEntryInternal(final String contactId,
|
||||
Method getEntityIteratorMethod) throws VCardException {
|
||||
final Method getEntityIteratorMethod) throws VCardException {
|
||||
final Map<String, List<ContentValues>> contentValuesListMap =
|
||||
new HashMap<String, List<ContentValues>>();
|
||||
// The resolver may return the entity iterator with no data. It is possible.
|
||||
@@ -466,12 +526,13 @@ public class VCardComposer {
|
||||
EntityIterator entityIterator = null;
|
||||
try {
|
||||
final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon()
|
||||
// .appendQueryParameter("for_export_only", "1")
|
||||
.appendQueryParameter(Data.FOR_EXPORT_ONLY, "1")
|
||||
.build();
|
||||
final String selection = Data.CONTACT_ID + "=?";
|
||||
final String[] selectionArgs = new String[] {contactId};
|
||||
if (getEntityIteratorMethod != null) {
|
||||
// Please note that this branch is executed by some tests only
|
||||
// Please note that this branch is executed by unit tests only
|
||||
try {
|
||||
entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
|
||||
mContentResolver, uri, selection, selectionArgs, null);
|
||||
@@ -527,22 +588,33 @@ public class VCardComposer {
|
||||
}
|
||||
}
|
||||
|
||||
final VCardBuilder builder = new VCardBuilder(mVCardType);
|
||||
builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
|
||||
.appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
|
||||
.appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
|
||||
.appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
|
||||
.appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
|
||||
.appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
|
||||
.appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE));
|
||||
if ((mVCardType & VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT) == 0) {
|
||||
builder.appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE));
|
||||
return buildVCard(contentValuesListMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in
|
||||
* {ContactsContract}. Developers can override this method to customize the output.
|
||||
*/
|
||||
public String buildVCard(final Map<String, List<ContentValues>> contentValuesListMap) {
|
||||
if (contentValuesListMap == null) {
|
||||
Log.e(LOG_TAG, "The given map is null. Ignore and return empty String");
|
||||
return "";
|
||||
} else {
|
||||
final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset);
|
||||
builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
|
||||
.appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
|
||||
.appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
|
||||
.appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
|
||||
.appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
|
||||
.appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
|
||||
.appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE))
|
||||
.appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE))
|
||||
.appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
|
||||
.appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
|
||||
.appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
|
||||
.appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
|
||||
return builder.toString();
|
||||
}
|
||||
builder.appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
|
||||
.appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
|
||||
.appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
|
||||
.appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
@@ -565,26 +637,38 @@ public class VCardComposer {
|
||||
@Override
|
||||
public void finalize() {
|
||||
if (!mTerminateIsCalled) {
|
||||
Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step.");
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return returns the number of available entities. The return value is undefined
|
||||
* when this object is not ready yet (typically when {{@link #init()} is not called
|
||||
* or when {@link #terminate()} is already called).
|
||||
*/
|
||||
public int getCount() {
|
||||
if (mCursor == null) {
|
||||
Log.w(LOG_TAG, "This object is not ready yet.");
|
||||
return 0;
|
||||
}
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when there's no entity to be built. The return value is undefined
|
||||
* when this object is not ready yet.
|
||||
*/
|
||||
public boolean isAfterLast() {
|
||||
if (mCursor == null) {
|
||||
Log.w(LOG_TAG, "This object is not ready yet.");
|
||||
return false;
|
||||
}
|
||||
return mCursor.isAfterLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Return the error reason if possible.
|
||||
* @return Returns the error reason.
|
||||
*/
|
||||
public String getErrorReason() {
|
||||
return mErrorReason;
|
||||
|
||||
@@ -38,20 +38,43 @@ public class VCardConfig {
|
||||
|
||||
/* package */ static final int LOG_LEVEL = LOG_LEVEL_NONE;
|
||||
|
||||
/* package */ static final int PARSE_TYPE_UNKNOWN = 0;
|
||||
/* package */ static final int PARSE_TYPE_APPLE = 1;
|
||||
/* package */ static final int PARSE_TYPE_MOBILE_PHONE_JP = 2; // For Japanese mobile phones.
|
||||
/* package */ static final int PARSE_TYPE_FOMA = 3; // For Japanese FOMA mobile phones.
|
||||
/* package */ static final int PARSE_TYPE_WINDOWS_MOBILE_JP = 4;
|
||||
/**
|
||||
* <p>
|
||||
* The charset used during import.
|
||||
* </p>
|
||||
* <p>
|
||||
* We cannot determine which charset should be used to interpret lines in vCard,
|
||||
* while Java requires us to specify it when InputStream is used.
|
||||
* We need to rely on the mechanism due to some performance reason.
|
||||
* </p>
|
||||
* <p>
|
||||
* In order to avoid "misinterpretation" of charset and lose any data in vCard,
|
||||
* "ISO-8859-1" is first used for reading the stream.
|
||||
* When a charset is specified in a property (with "CHARSET=..." parameter),
|
||||
* the string is decoded to raw bytes and encoded into the specific charset,
|
||||
* </p>
|
||||
* <p>
|
||||
* Unicode specification there's a one to one mapping between each byte in ISO-8859-1
|
||||
* and a codepoint, and Java specification requires runtime must have the charset.
|
||||
* Thus, ISO-8859-1 is one effective mapping for intermediate mapping.
|
||||
* </p>
|
||||
*/
|
||||
public static final String DEFAULT_INTERMEDIATE_CHARSET = "ISO-8859-1";
|
||||
|
||||
// Assumes that "iso-8859-1" is able to map "all" 8bit characters to some unicode and
|
||||
// decode the unicode to the original charset. If not, this setting will cause some bug.
|
||||
public static final String DEFAULT_CHARSET = "iso-8859-1";
|
||||
|
||||
public static final int FLAG_V21 = 0;
|
||||
public static final int FLAG_V30 = 1;
|
||||
/**
|
||||
* The charset used when there's no information affbout what charset should be used to
|
||||
* encode the binary given from vCard.
|
||||
*/
|
||||
public static final String DEFAULT_IMPORT_CHARSET = "UTF-8";
|
||||
public static final String DEFAULT_EXPORT_CHARSET = "UTF-8";
|
||||
|
||||
// 0x2 is reserved for the future use ...
|
||||
/**
|
||||
* Do not use statically like "version == VERSION_V21"
|
||||
*/
|
||||
public static final int VERSION_21 = 0;
|
||||
public static final int VERSION_30 = 1;
|
||||
public static final int VERSION_40 = 2;
|
||||
public static final int VERSION_MASK = 3;
|
||||
|
||||
public static final int NAME_ORDER_DEFAULT = 0;
|
||||
public static final int NAME_ORDER_EUROPE = 0x4;
|
||||
@@ -59,144 +82,140 @@ public class VCardConfig {
|
||||
private static final int NAME_ORDER_MASK = 0xC;
|
||||
|
||||
// 0x10 is reserved for safety
|
||||
|
||||
private static final int FLAG_CHARSET_UTF8 = 0;
|
||||
private static final int FLAG_CHARSET_SHIFT_JIS = 0x100;
|
||||
private static final int FLAG_CHARSET_MASK = 0xF00;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The flag indicating the vCard composer will add some "X-" properties used only in Android
|
||||
* when the formal vCard specification does not have appropriate fields for that data.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* For example, Android accepts nickname information while vCard 2.1 does not.
|
||||
* When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME")
|
||||
* instead of just dropping it.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* vCard parser code automatically parses the field emitted even when this flag is off.
|
||||
*
|
||||
* Note that this flag does not assure all the information must be hold in the emitted vCard.
|
||||
* </p>
|
||||
*/
|
||||
private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The flag indicating the vCard composer will add some "X-" properties seen in the
|
||||
* vCard data emitted by the other softwares/devices when the formal vCard specification
|
||||
* does not have appropriate field(s) for that data.
|
||||
*
|
||||
* does not have appropriate field(s) for that data.
|
||||
* </p>
|
||||
* <p>
|
||||
* One example is X-PHONETIC-FIRST-NAME/X-PHONETIC-MIDDLE-NAME/X-PHONETIC-LAST-NAME, which are
|
||||
* for phonetic name (how the name is pronounced), seen in the vCard emitted by some other
|
||||
* non-Android devices/softwares. We chose to enable the vCard composer to use those
|
||||
* defact properties since they are also useful for Android devices.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* Note for developers: only "X-" properties should be added with this flag. vCard 2.1/3.0
|
||||
* allows any kind of "X-" properties but does not allow non-"X-" properties (except IANA tokens
|
||||
* in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties.
|
||||
* </p>
|
||||
*/
|
||||
private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000;
|
||||
|
||||
/**
|
||||
* The flag indicating some specific dialect seen in vcard of DoCoMo (one of Japanese
|
||||
* <p>
|
||||
* The flag indicating some specific dialect seen in vCard of DoCoMo (one of Japanese
|
||||
* mobile careers) should be used. This flag does not include any other information like
|
||||
* that "the vCard is for Japanese". So it is "possible" that "the vCard should have DoCoMo's
|
||||
* dialect but the name order should be European", but it is not recommended.
|
||||
* </p>
|
||||
*/
|
||||
private static final int FLAG_DOCOMO = 0x20000000;
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <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>
|
||||
* </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>
|
||||
* </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 related names 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>
|
||||
* <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>
|
||||
* </p>
|
||||
* <p>
|
||||
* This flag is useful when some target importer you are going to focus on does not accept
|
||||
* such properties with Quoted-Printable encoding.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Again, we should not use this flag at all for complying vCard 2.1 spec.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
|
||||
* kind of problem (hopefully).
|
||||
* </P>
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000;
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <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>
|
||||
* </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>
|
||||
* </p>
|
||||
*/
|
||||
public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x0800000;
|
||||
public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x08000000;
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <p>
|
||||
* The flag indicating the vCard composer "for 2.1" emits "TYPE=" string toward TYPE params
|
||||
* every time possible. The default behavior does not emit it and is valid in the spec.
|
||||
* In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in vCard 3.0 specification.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Detail:
|
||||
* How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0.
|
||||
* </p>
|
||||
* <P>
|
||||
* e.g.<BR />
|
||||
* 1) Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."<BR />
|
||||
* 2) Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."<BR />
|
||||
* 3) Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."<BR />
|
||||
* </P>
|
||||
* <P>
|
||||
* 2) had been the default of VCard exporter/importer in Android, but it is found that
|
||||
* some external exporter is not able to parse the type format like 2) but only 3).
|
||||
* </P>
|
||||
* <P>
|
||||
* <p>
|
||||
* e.g.
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."</li>
|
||||
* <li>Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."</li>
|
||||
* <li>Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* If you are targeting to the importer which cannot accept TYPE params without "TYPE="
|
||||
* strings (which should be rare though), please use this flag.
|
||||
* </P>
|
||||
* <P>
|
||||
* Example usage: int vcardType = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
|
||||
* </P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Example usage:
|
||||
* <pre class="prettyprint">int type = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);</pre>
|
||||
* </p>
|
||||
*/
|
||||
public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* The flag asking exporter to refrain image export.
|
||||
* </P>
|
||||
* @hide will be deleted in the near future.
|
||||
*/
|
||||
public static final int FLAG_REFRAIN_IMAGE_EXPORT = 0x02000000;
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <p>
|
||||
* The flag indicating the vCard composer does touch nothing toward phone number Strings
|
||||
* but leave it as is.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* The vCard specifications mention nothing toward phone numbers, while some devices
|
||||
* do (wrongly, but with innevitable reasons).
|
||||
* For example, there's a possibility Japanese mobile phones are expected to have
|
||||
@@ -207,187 +226,194 @@ public class VCardConfig {
|
||||
* becomes "111-222-3333").
|
||||
* Unfortunate side effect of that use was some control characters used in the other
|
||||
* areas may be badly affected by the formatting.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* This flag disables that formatting, affecting both importer and exporter.
|
||||
* If the user is aware of some side effects due to the implicit formatting, use this flag.
|
||||
* </P>
|
||||
* </p>
|
||||
*/
|
||||
public static final int FLAG_REFRAIN_PHONE_NUMBER_FORMATTING = 0x02000000;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* For importer only. Ignored in exporter.
|
||||
* </p>
|
||||
* <p>
|
||||
* The flag indicating the parser should handle a nested vCard, in which vCard clause starts
|
||||
* in another vCard clause. Here's a typical example.
|
||||
* </p>
|
||||
* <pre class="prettyprint">BEGIN:VCARD
|
||||
* BEGIN:VCARD
|
||||
* VERSION:2.1
|
||||
* ...
|
||||
* END:VCARD
|
||||
* END:VCARD</pre>
|
||||
* <p>
|
||||
* The vCard 2.1 specification allows the nest, but also let parsers ignore nested entries,
|
||||
* while some mobile devices emit nested ones as primary data to be imported.
|
||||
* </p>
|
||||
* <p>
|
||||
* This flag forces a vCard parser to torelate such a nest and understand its content.
|
||||
* </p>
|
||||
*/
|
||||
public static final int FLAG_TORELATE_NEST = 0x01000000;
|
||||
|
||||
//// The followings are VCard types available from importer/exporter. ////
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* Generic vCard format with the vCard 2.1. Uses UTF-8 for the charset.
|
||||
* When composing a vCard entry, the US convension will be used toward formatting
|
||||
* some values.
|
||||
* </P>
|
||||
* <P>
|
||||
* <p>
|
||||
* The type indicating nothing. Used by {@link VCardSourceDetector} when it
|
||||
* was not able to guess the exact vCard type.
|
||||
* </p>
|
||||
*/
|
||||
public static final int VCARD_TYPE_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Generic vCard format with the vCard 2.1. 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 it should be "Prefix Family Middle Given Suffix" in Japan for example.
|
||||
* </P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Uses UTF-8 for the charset as a charset for exporting. Note that old vCard importer
|
||||
* outside Android cannot accept it since vCard 2.1 specifically does not allow
|
||||
* that charset, while we need to use it to support various languages around the world.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you want to use alternative charset, you should notify the charset to the other
|
||||
* compontent to be used.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
public static final int VCARD_TYPE_V21_GENERIC =
|
||||
(VERSION_21 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static String VCARD_TYPE_V21_GENERIC_UTF8_STR = "v21_generic";
|
||||
/* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <p>
|
||||
* General vCard format with the version 3.0. Uses UTF-8 for the charset.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Not fully ready yet. Use with caution when you use this.
|
||||
* </P>
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
public static final int VCARD_TYPE_V30_GENERIC =
|
||||
(VERSION_30 | NAME_ORDER_DEFAULT | 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";
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* General vCard format with the version 4.0.
|
||||
* @hide vCard 4.0 is not published yet.
|
||||
*/
|
||||
public static final int VCARD_TYPE_V40_GENERIC =
|
||||
(VERSION_40 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V40_GENERIC_STR = "v40_generic";
|
||||
|
||||
/**
|
||||
* <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>
|
||||
* </p>
|
||||
*/
|
||||
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_UTF8_STR = "v21_europe";
|
||||
public static final int VCARD_TYPE_V21_EUROPE =
|
||||
(VERSION_21 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <p>
|
||||
* General vCard format with the version 3.0 with some Europe convension. Uses UTF-8.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Not ready yet. Use with caution when you use this.
|
||||
* </P>
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
public static final int VCARD_TYPE_V30_EUROPE =
|
||||
(VERSION_30 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <p>
|
||||
* The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Not ready yet. Use with caution when you use this.
|
||||
* </P>
|
||||
* </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);
|
||||
public static final int VCARD_TYPE_V21_JAPANESE =
|
||||
(VERSION_21 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8";
|
||||
/* package */ static final String VCARD_TYPE_V21_JAPANESE_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_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_SJIS_STR = "v30_japanese_sjis";
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <p>
|
||||
* The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
|
||||
* </P>
|
||||
* <P>
|
||||
* </p>
|
||||
* <p>
|
||||
* Not ready yet. Use with caution when you use this.
|
||||
* </P>
|
||||
* </p>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
|
||||
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
public static final int VCARD_TYPE_V30_JAPANESE =
|
||||
(VERSION_30 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8";
|
||||
/* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese_utf8";
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <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>
|
||||
* possible, etc. It would be better to use Shift_JIS as a charset for maximum
|
||||
* compatibility.
|
||||
* </p>
|
||||
* @hide Should not be available world wide.
|
||||
*/
|
||||
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_NAME_PROPERTIES);
|
||||
(VERSION_21 | NAME_ORDER_JAPANESE |
|
||||
FLAG_CONVERT_PHONETIC_NAME_STRINGS | FLAG_REFRAIN_QP_TO_NAME_PROPERTIES);
|
||||
|
||||
/* package */ 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>
|
||||
* The vCard format used in DoCoMo, which is one of Japanese mobile phone careers.
|
||||
* </p>
|
||||
* <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>
|
||||
* </p>
|
||||
* @hide Should not be available world wide.
|
||||
*/
|
||||
public static final int VCARD_TYPE_DOCOMO =
|
||||
(VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_DOCOMO_STR = "docomo";
|
||||
|
||||
public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC_UTF8;
|
||||
public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC;
|
||||
|
||||
private static final Map<String, Integer> sVCardTypeMap;
|
||||
private static final Set<Integer> sJapaneseMobileTypeSet;
|
||||
|
||||
static {
|
||||
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_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
|
||||
sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
|
||||
sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
|
||||
sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
|
||||
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
|
||||
sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
|
||||
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);
|
||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE);
|
||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
|
||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
|
||||
}
|
||||
@@ -404,20 +430,20 @@ public class VCardConfig {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isV30(final int vcardType) {
|
||||
return ((vcardType & FLAG_V30) != 0);
|
||||
public static boolean isVersion21(final int vcardType) {
|
||||
return (vcardType & VERSION_MASK) == VERSION_21;
|
||||
}
|
||||
|
||||
public static boolean isVersion30(final int vcardType) {
|
||||
return (vcardType & VERSION_MASK) == VERSION_30;
|
||||
}
|
||||
|
||||
public static boolean isVersion40(final int vcardType) {
|
||||
return (vcardType & VERSION_MASK) == VERSION_40;
|
||||
}
|
||||
|
||||
public static boolean shouldUseQuotedPrintable(final int vcardType) {
|
||||
return !isV30(vcardType);
|
||||
}
|
||||
|
||||
public static boolean usesUtf8(final int vcardType) {
|
||||
return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_UTF8);
|
||||
}
|
||||
|
||||
public static boolean usesShiftJis(final int vcardType) {
|
||||
return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_SHIFT_JIS);
|
||||
return !isVersion30(vcardType);
|
||||
}
|
||||
|
||||
public static int getNameOrderType(final int vcardType) {
|
||||
@@ -442,7 +468,7 @@ public class VCardConfig {
|
||||
}
|
||||
|
||||
public static boolean appendTypeParamName(final int vcardType) {
|
||||
return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0));
|
||||
return (isVersion30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -474,4 +500,4 @@ public class VCardConfig {
|
||||
|
||||
private VCardConfig() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ package android.pim.vcard;
|
||||
public class VCardConstants {
|
||||
public static final String VERSION_V21 = "2.1";
|
||||
public static final String VERSION_V30 = "3.0";
|
||||
public static final String VERSION_V40 = "4.0";
|
||||
|
||||
// The property names valid both in vCard 2.1 and 3.0.
|
||||
public static final String PROPERTY_BEGIN = "BEGIN";
|
||||
@@ -38,25 +39,30 @@ public class VCardConstants {
|
||||
public static final String PROPERTY_PHOTO = "PHOTO";
|
||||
public static final String PROPERTY_LOGO = "LOGO";
|
||||
public static final String PROPERTY_URL = "URL";
|
||||
public static final String PROPERTY_BDAY = "BDAY"; // Birthday
|
||||
public static final String PROPERTY_BDAY = "BDAY"; // Birthday (3.0, 4.0)
|
||||
public static final String PROPERTY_BIRTH = "BIRTH"; // Place of birth (4.0)
|
||||
public static final String PROPERTY_ANNIVERSARY = "ANNIVERSARY"; // Date of marriage (4.0)
|
||||
public static final String PROPERTY_NAME = "NAME"; // (3.0, 4,0)
|
||||
public static final String PROPERTY_NICKNAME = "NICKNAME"; // (3.0, 4.0)
|
||||
public static final String PROPERTY_SORT_STRING = "SORT-STRING"; // (3.0, 4.0)
|
||||
public static final String PROPERTY_END = "END";
|
||||
|
||||
// Valid property names not supported (not appropriately handled) by our vCard importer now.
|
||||
// Valid property names not supported (not appropriately handled) by our importer.
|
||||
// TODO: Should be removed from the view of memory efficiency?
|
||||
public static final String PROPERTY_REV = "REV";
|
||||
public static final String PROPERTY_AGENT = "AGENT";
|
||||
public static final String PROPERTY_AGENT = "AGENT"; // (3.0)
|
||||
public static final String PROPERTY_DDAY = "DDAY"; // Date of death (4.0)
|
||||
public static final String PROPERTY_DEATH = "DEATH"; // Place of death (4.0)
|
||||
|
||||
// Available in vCard 3.0. Shoud not use when composing vCard 2.1 file.
|
||||
public static final String PROPERTY_NAME = "NAME";
|
||||
public static final String PROPERTY_NICKNAME = "NICKNAME";
|
||||
public static final String PROPERTY_SORT_STRING = "SORT-STRING";
|
||||
|
||||
// De-fact property values expressing phonetic names.
|
||||
public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
|
||||
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 ContactsStruct in Eclair and de-fact vCard extensions
|
||||
// shown in http://en.wikipedia.org/wiki/VCard support are defined here.
|
||||
// Properties both ContactsStruct 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";
|
||||
public static final String PROPERTY_X_YAHOO = "X-YAHOO";
|
||||
@@ -89,6 +95,9 @@ public class VCardConstants {
|
||||
public static final String PARAM_TYPE_VOICE = "VOICE";
|
||||
public static final String PARAM_TYPE_INTERNET = "INTERNET";
|
||||
|
||||
public static final String PARAM_CHARSET = "CHARSET";
|
||||
public static final String PARAM_ENCODING = "ENCODING";
|
||||
|
||||
// Abbreviation of "prefered" according to vCard 2.1 specification.
|
||||
// We interpret this value as "primary" property during import/export.
|
||||
//
|
||||
@@ -109,6 +118,12 @@ public class VCardConstants {
|
||||
public static final String PARAM_TYPE_BBS = "BBS";
|
||||
public static final String PARAM_TYPE_VIDEO = "VIDEO";
|
||||
|
||||
public static final String PARAM_ENCODING_7BIT = "7BIT";
|
||||
public static final String PARAM_ENCODING_8BIT = "8BIT";
|
||||
public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE";
|
||||
public static final String PARAM_ENCODING_BASE64 = "BASE64"; // Available in vCard 2.1
|
||||
public static final String PARAM_ENCODING_B = "B"; // Available in vCard 3.0
|
||||
|
||||
// TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1).
|
||||
// These types are basically encoded to "X-" parameters when composing vCard.
|
||||
// Parser passes these when "X-" is added to the parameter or not.
|
||||
@@ -126,14 +141,15 @@ public class VCardConstants {
|
||||
public static final String PARAM_ADR_TYPE_DOM = "DOM";
|
||||
public static final String PARAM_ADR_TYPE_INTL = "INTL";
|
||||
|
||||
public static final String PARAM_LANGUAGE = "LANGUAGE";
|
||||
|
||||
// SORT-AS parameter introduced in vCard 4.0 (as of rev.13)
|
||||
public static final String PARAM_SORT_AS = "SORT-AS";
|
||||
|
||||
// TYPE parameters not officially valid but used in some vCard exporter.
|
||||
// Do not use in composer side.
|
||||
public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY";
|
||||
|
||||
// DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of SORT-STRING in
|
||||
// vCard 3.0.
|
||||
public static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";
|
||||
|
||||
public interface ImportOnly {
|
||||
public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
|
||||
// Some device emits this "X-" parameter for expressing Google Talk,
|
||||
@@ -142,7 +158,14 @@ public class VCardConstants {
|
||||
public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
|
||||
}
|
||||
|
||||
/* package */ static final int MAX_DATA_COLUMN = 15;
|
||||
//// Mainly for package constants.
|
||||
|
||||
// DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of
|
||||
// SORT-STRING invCard 3.0.
|
||||
/* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";
|
||||
|
||||
// Used in unit test.
|
||||
public static final int MAX_DATA_COLUMN = 15;
|
||||
|
||||
/* package */ static final int MAX_CHARACTER_NUMS_QP = 76;
|
||||
static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75;
|
||||
|
||||
@@ -20,13 +20,11 @@ import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.Groups;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||
@@ -61,9 +59,6 @@ public class VCardEntry {
|
||||
|
||||
private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
|
||||
|
||||
private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
|
||||
private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
|
||||
|
||||
private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
|
||||
|
||||
static {
|
||||
@@ -78,12 +73,12 @@ public class VCardEntry {
|
||||
Im.PROTOCOL_GOOGLE_TALK);
|
||||
}
|
||||
|
||||
static public class PhoneData {
|
||||
public static class PhoneData {
|
||||
public final int type;
|
||||
public final String data;
|
||||
public final String label;
|
||||
// isPrimary is changable only when there's no appropriate one existing in
|
||||
// the original VCard.
|
||||
// isPrimary is (not final but) changable, only when there's no appropriate one existing
|
||||
// in the original VCard.
|
||||
public boolean isPrimary;
|
||||
public PhoneData(int type, String data, String label, boolean isPrimary) {
|
||||
this.type = type;
|
||||
@@ -109,13 +104,11 @@ public class VCardEntry {
|
||||
}
|
||||
}
|
||||
|
||||
static public class EmailData {
|
||||
public static class EmailData {
|
||||
public final int type;
|
||||
public final String data;
|
||||
// Used only when TYPE is TYPE_CUSTOM.
|
||||
public final String label;
|
||||
// isPrimary is changable only when there's no appropriate one existing in
|
||||
// the original VCard.
|
||||
public boolean isPrimary;
|
||||
public EmailData(int type, String data, String label, boolean isPrimary) {
|
||||
this.type = type;
|
||||
@@ -141,9 +134,9 @@ public class VCardEntry {
|
||||
}
|
||||
}
|
||||
|
||||
static public class PostalData {
|
||||
// Determined by vCard spec.
|
||||
// PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
|
||||
public static class PostalData {
|
||||
// Determined by vCard specification.
|
||||
// - PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
|
||||
public static final int ADDR_MAX_DATA_SIZE = 7;
|
||||
private final String[] dataArray;
|
||||
public final String pobox;
|
||||
@@ -248,24 +241,28 @@ public class VCardEntry {
|
||||
}
|
||||
}
|
||||
|
||||
static public class OrganizationData {
|
||||
public static class OrganizationData {
|
||||
public final int type;
|
||||
// non-final is Intentional: we may change the values since this info is separated into
|
||||
// two parts in vCard: "ORG" + "TITLE".
|
||||
// two parts in vCard: "ORG" + "TITLE", and we have to cope with each field in
|
||||
// different timing.
|
||||
public String companyName;
|
||||
public String departmentName;
|
||||
public String titleName;
|
||||
public final String phoneticName; // We won't have this in "TITLE" property.
|
||||
public boolean isPrimary;
|
||||
|
||||
public OrganizationData(int type,
|
||||
String companyName,
|
||||
String departmentName,
|
||||
String titleName,
|
||||
boolean isPrimary) {
|
||||
final String companyName,
|
||||
final String departmentName,
|
||||
final String titleName,
|
||||
final String phoneticName,
|
||||
final boolean isPrimary) {
|
||||
this.type = type;
|
||||
this.companyName = companyName;
|
||||
this.departmentName = departmentName;
|
||||
this.titleName = titleName;
|
||||
this.phoneticName = phoneticName;
|
||||
this.isPrimary = isPrimary;
|
||||
}
|
||||
|
||||
@@ -313,7 +310,7 @@ public class VCardEntry {
|
||||
}
|
||||
}
|
||||
|
||||
static public class ImData {
|
||||
public static class ImData {
|
||||
public final int protocol;
|
||||
public final String customProtocol;
|
||||
public final int type;
|
||||
@@ -434,6 +431,14 @@ public class VCardEntry {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(dmiyakawa): vCard 4.0 logically has multiple formatted names and we need to
|
||||
// select the most preferable one using PREF parameter.
|
||||
//
|
||||
// e.g. (based on rev.13)
|
||||
// FN;PREF=1:John M. Doe
|
||||
// FN;PREF=2:John Doe
|
||||
// FN;PREF=3;John
|
||||
|
||||
private String mFamilyName;
|
||||
private String mGivenName;
|
||||
private String mMiddleName;
|
||||
@@ -441,7 +446,7 @@ public class VCardEntry {
|
||||
private String mSuffix;
|
||||
|
||||
// Used only when no family nor given name is found.
|
||||
private String mFullName;
|
||||
private String mFormattedName;
|
||||
|
||||
private String mPhoneticFamilyName;
|
||||
private String mPhoneticGivenName;
|
||||
@@ -454,6 +459,7 @@ public class VCardEntry {
|
||||
private String mDisplayName;
|
||||
|
||||
private String mBirthday;
|
||||
private String mAnniversary;
|
||||
|
||||
private List<String> mNoteList;
|
||||
private List<PhoneData> mPhoneList;
|
||||
@@ -469,7 +475,7 @@ public class VCardEntry {
|
||||
private final Account mAccount;
|
||||
|
||||
public VCardEntry() {
|
||||
this(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
|
||||
this(VCardConfig.VCARD_TYPE_V21_GENERIC);
|
||||
}
|
||||
|
||||
public VCardEntry(int vcardType) {
|
||||
@@ -499,7 +505,6 @@ public class VCardEntry {
|
||||
}
|
||||
}
|
||||
|
||||
// Use NANP in default when there's no information about locale.
|
||||
final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
|
||||
formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
|
||||
}
|
||||
@@ -529,22 +534,44 @@ public class VCardEntry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called via {@link #handleOrgValue(int, List, boolean)} or
|
||||
* Should be called via {@link #handleOrgValue(int, List, Map, boolean) or
|
||||
* {@link #handleTitleValue(String)}.
|
||||
*/
|
||||
private void addNewOrganization(int type, final String companyName,
|
||||
final String departmentName,
|
||||
final String titleName, boolean isPrimary) {
|
||||
final String titleName,
|
||||
final String phoneticName,
|
||||
final boolean isPrimary) {
|
||||
if (mOrganizationList == null) {
|
||||
mOrganizationList = new ArrayList<OrganizationData>();
|
||||
}
|
||||
mOrganizationList.add(new OrganizationData(type, companyName,
|
||||
departmentName, titleName, isPrimary));
|
||||
departmentName, titleName, phoneticName, isPrimary));
|
||||
}
|
||||
|
||||
private static final List<String> sEmptyList =
|
||||
Collections.unmodifiableList(new ArrayList<String>(0));
|
||||
|
||||
private String buildSinglePhoneticNameFromSortAsParam(Map<String, Collection<String>> paramMap) {
|
||||
final Collection<String> sortAsCollection = paramMap.get(VCardConstants.PARAM_SORT_AS);
|
||||
if (sortAsCollection != null && sortAsCollection.size() != 0) {
|
||||
if (sortAsCollection.size() > 1) {
|
||||
Log.w(LOG_TAG, "Incorrect multiple SORT_AS parameters detected: " +
|
||||
Arrays.toString(sortAsCollection.toArray()));
|
||||
}
|
||||
final List<String> sortNames =
|
||||
VCardUtils.constructListFromValue(sortAsCollection.iterator().next(),
|
||||
mVCardType);
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (final String elem : sortNames) {
|
||||
builder.append(elem);
|
||||
}
|
||||
return builder.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set "ORG" related values to the appropriate data. If there's more than one
|
||||
* {@link OrganizationData} objects, this input data are attached to the last one which
|
||||
@@ -552,7 +579,9 @@ public class VCardEntry {
|
||||
* {@link OrganizationData} object, a new {@link OrganizationData} is created,
|
||||
* whose title is set to null.
|
||||
*/
|
||||
private void handleOrgValue(final int type, List<String> orgList, boolean isPrimary) {
|
||||
private void handleOrgValue(final int type, List<String> orgList,
|
||||
Map<String, Collection<String>> paramMap, boolean isPrimary) {
|
||||
final String phoneticName = buildSinglePhoneticNameFromSortAsParam(paramMap);
|
||||
if (orgList == null) {
|
||||
orgList = sEmptyList;
|
||||
}
|
||||
@@ -587,7 +616,7 @@ public class VCardEntry {
|
||||
if (mOrganizationList == null) {
|
||||
// Create new first organization entry, with "null" title which may be
|
||||
// added via handleTitleValue().
|
||||
addNewOrganization(type, companyName, departmentName, null, isPrimary);
|
||||
addNewOrganization(type, companyName, departmentName, null, phoneticName, isPrimary);
|
||||
return;
|
||||
}
|
||||
for (OrganizationData organizationData : mOrganizationList) {
|
||||
@@ -605,7 +634,7 @@ public class VCardEntry {
|
||||
}
|
||||
// No OrganizatioData is available. Create another one, with "null" title, which may be
|
||||
// added via handleTitleValue().
|
||||
addNewOrganization(type, companyName, departmentName, null, isPrimary);
|
||||
addNewOrganization(type, companyName, departmentName, null, phoneticName, isPrimary);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -619,7 +648,7 @@ public class VCardEntry {
|
||||
if (mOrganizationList == null) {
|
||||
// Create new first organization entry, with "null" other info, which may be
|
||||
// added via handleOrgValue().
|
||||
addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false);
|
||||
addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, null, false);
|
||||
return;
|
||||
}
|
||||
for (OrganizationData organizationData : mOrganizationList) {
|
||||
@@ -630,7 +659,7 @@ public class VCardEntry {
|
||||
}
|
||||
// No Organization is available. Create another one, with "null" other info, which may be
|
||||
// added via handleOrgValue().
|
||||
addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false);
|
||||
addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, null, false);
|
||||
}
|
||||
|
||||
private void addIm(int protocol, String customProtocol, int type,
|
||||
@@ -656,11 +685,54 @@ public class VCardEntry {
|
||||
mPhotoList.add(photoData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to extract paramMap, constructs SORT-AS parameter values, and store them in
|
||||
* appropriate phonetic name variables.
|
||||
*
|
||||
* This method does not care the vCard version. Even when we have SORT-AS parameters in
|
||||
* invalid versions (i.e. 2.1 and 3.0), we scilently accept them so that we won't drop
|
||||
* meaningful information. If we had this parameter in the N field of vCard 3.0, and
|
||||
* the contact data also have SORT-STRING, we will prefer SORT-STRING, since it is
|
||||
* regitimate property to be understood.
|
||||
*/
|
||||
private void tryHandleSortAsName(final Map<String, Collection<String>> paramMap) {
|
||||
if (VCardConfig.isVersion30(mVCardType) &&
|
||||
!(TextUtils.isEmpty(mPhoneticFamilyName) &&
|
||||
TextUtils.isEmpty(mPhoneticMiddleName) &&
|
||||
TextUtils.isEmpty(mPhoneticGivenName))) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<String> sortAsCollection = paramMap.get(VCardConstants.PARAM_SORT_AS);
|
||||
if (sortAsCollection != null && sortAsCollection.size() != 0) {
|
||||
if (sortAsCollection.size() > 1) {
|
||||
Log.w(LOG_TAG, "Incorrect multiple SORT_AS parameters detected: " +
|
||||
Arrays.toString(sortAsCollection.toArray()));
|
||||
}
|
||||
final List<String> sortNames =
|
||||
VCardUtils.constructListFromValue(sortAsCollection.iterator().next(),
|
||||
mVCardType);
|
||||
int size = sortNames.size();
|
||||
if (size > 3) {
|
||||
size = 3;
|
||||
}
|
||||
switch (size) {
|
||||
case 3: mPhoneticMiddleName = sortNames.get(2); //$FALL-THROUGH$
|
||||
case 2: mPhoneticGivenName = sortNames.get(1); //$FALL-THROUGH$
|
||||
default: mPhoneticFamilyName = sortNames.get(0); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
private void handleNProperty(List<String> elems) {
|
||||
private void handleNProperty(final List<String> paramValues,
|
||||
Map<String, Collection<String>> paramMap) {
|
||||
// in vCard 4.0, SORT-AS parameter is available.
|
||||
tryHandleSortAsName(paramMap);
|
||||
|
||||
// Family, Given, Middle, Prefix, Suffix. (1 - 5)
|
||||
int size;
|
||||
if (elems == null || (size = elems.size()) < 1) {
|
||||
if (paramValues == null || (size = paramValues.size()) < 1) {
|
||||
return;
|
||||
}
|
||||
if (size > 5) {
|
||||
@@ -668,12 +740,12 @@ public class VCardEntry {
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
// fallthrough
|
||||
case 5: mSuffix = elems.get(4);
|
||||
case 4: mPrefix = elems.get(3);
|
||||
case 3: mMiddleName = elems.get(2);
|
||||
case 2: mGivenName = elems.get(1);
|
||||
default: mFamilyName = elems.get(0);
|
||||
// Fall-through.
|
||||
case 5: mSuffix = paramValues.get(4);
|
||||
case 4: mPrefix = paramValues.get(3);
|
||||
case 3: mMiddleName = paramValues.get(2);
|
||||
case 2: mGivenName = paramValues.get(1);
|
||||
default: mFamilyName = paramValues.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,13 +826,13 @@ public class VCardEntry {
|
||||
if (propName.equals(VCardConstants.PROPERTY_VERSION)) {
|
||||
// vCard version. Ignore this.
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_FN)) {
|
||||
mFullName = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFullName == null) {
|
||||
mFormattedName = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFormattedName == null) {
|
||||
// Only in vCard 3.0. Use this if FN, which must exist in vCard 3.0 but may not
|
||||
// actually exist in the real vCard data, does not exist.
|
||||
mFullName = propValue;
|
||||
mFormattedName = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_N)) {
|
||||
handleNProperty(propValueList);
|
||||
handleNProperty(propValueList, paramMap);
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
|
||||
mPhoneticFullName = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_NICKNAME) ||
|
||||
@@ -775,8 +847,7 @@ public class VCardEntry {
|
||||
// which is correct behavior from the view of vCard 2.1.
|
||||
// But we want it to be separated, so do the separation here.
|
||||
final List<String> phoneticNameList =
|
||||
VCardUtils.constructListFromValue(propValue,
|
||||
VCardConfig.isV30(mVCardType));
|
||||
VCardUtils.constructListFromValue(propValue, mVCardType);
|
||||
handlePhoneticNameFromSound(phoneticNameList);
|
||||
} else {
|
||||
// Ignore this field since Android cannot understand what it is.
|
||||
@@ -877,7 +948,7 @@ public class VCardEntry {
|
||||
}
|
||||
}
|
||||
}
|
||||
handleOrgValue(type, propValueList, isPrimary);
|
||||
handleOrgValue(type, propValueList, paramMap, isPrimary);
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_TITLE)) {
|
||||
handleTitleValue(propValue);
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_ROLE)) {
|
||||
@@ -966,6 +1037,8 @@ public class VCardEntry {
|
||||
mWebsiteList.add(propValue);
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_BDAY)) {
|
||||
mBirthday = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_ANNIVERSARY)) {
|
||||
mAnniversary = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
|
||||
mPhoneticGivenName = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
|
||||
@@ -974,33 +1047,9 @@ public class VCardEntry {
|
||||
mPhoneticFamilyName = propValue;
|
||||
} else if (propName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) {
|
||||
final List<String> customPropertyList =
|
||||
VCardUtils.constructListFromValue(propValue,
|
||||
VCardConfig.isV30(mVCardType));
|
||||
VCardUtils.constructListFromValue(propValue, mVCardType);
|
||||
handleAndroidCustomProperty(customPropertyList);
|
||||
/*} else if (propName.equals("REV")) {
|
||||
// Revision of this VCard entry. I think we can ignore this.
|
||||
} else if (propName.equals("UID")) {
|
||||
} else if (propName.equals("KEY")) {
|
||||
// Type is X509 or PGP? I don't know how to handle this...
|
||||
} else if (propName.equals("MAILER")) {
|
||||
} else if (propName.equals("TZ")) {
|
||||
} else if (propName.equals("GEO")) {
|
||||
} else if (propName.equals("CLASS")) {
|
||||
// vCard 3.0 only.
|
||||
// e.g. CLASS:CONFIDENTIAL
|
||||
} else if (propName.equals("PROFILE")) {
|
||||
// VCard 3.0 only. Must be "VCARD". I think we can ignore this.
|
||||
} else if (propName.equals("CATEGORIES")) {
|
||||
// VCard 3.0 only.
|
||||
// e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY
|
||||
} else if (propName.equals("SOURCE")) {
|
||||
// VCard 3.0 only.
|
||||
} else if (propName.equals("PRODID")) {
|
||||
// VCard 3.0 only.
|
||||
// To specify the identifier for the product that created
|
||||
// the vCard object.*/
|
||||
} else {
|
||||
// Unknown X- words and IANA token.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,8 +1065,8 @@ public class VCardEntry {
|
||||
*/
|
||||
private void constructDisplayName() {
|
||||
// FullName (created via "FN" or "NAME" field) is prefered.
|
||||
if (!TextUtils.isEmpty(mFullName)) {
|
||||
mDisplayName = mFullName;
|
||||
if (!TextUtils.isEmpty(mFormattedName)) {
|
||||
mDisplayName = mFormattedName;
|
||||
} else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
|
||||
mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
|
||||
mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix);
|
||||
@@ -1062,23 +1111,6 @@ public class VCardEntry {
|
||||
if (mAccount != null) {
|
||||
builder.withValue(RawContacts.ACCOUNT_NAME, mAccount.name);
|
||||
builder.withValue(RawContacts.ACCOUNT_TYPE, mAccount.type);
|
||||
|
||||
// Assume that caller side creates this group if it does not exist.
|
||||
if (ACCOUNT_TYPE_GOOGLE.equals(mAccount.type)) {
|
||||
final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] {
|
||||
Groups.SOURCE_ID },
|
||||
Groups.TITLE + "=?", new String[] {
|
||||
GOOGLE_MY_CONTACTS_GROUP }, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
myGroupsId = cursor.getString(0);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.withValue(RawContacts.ACCOUNT_NAME, null);
|
||||
builder.withValue(RawContacts.ACCOUNT_TYPE, null);
|
||||
@@ -1154,6 +1186,9 @@ public class VCardEntry {
|
||||
if (organizationData.titleName != null) {
|
||||
builder.withValue(Organization.TITLE, organizationData.titleName);
|
||||
}
|
||||
if (organizationData.phoneticName != null) {
|
||||
builder.withValue(Organization.PHONETIC_NAME, organizationData.phoneticName);
|
||||
}
|
||||
if (organizationData.isPrimary) {
|
||||
builder.withValue(Organization.IS_PRIMARY, 1);
|
||||
}
|
||||
@@ -1251,6 +1286,15 @@ public class VCardEntry {
|
||||
operationList.add(builder.build());
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mAnniversary)) {
|
||||
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
|
||||
builder.withValueBackReference(Event.RAW_CONTACT_ID, 0);
|
||||
builder.withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
|
||||
builder.withValue(Event.START_DATE, mAnniversary);
|
||||
builder.withValue(Event.TYPE, Event.TYPE_ANNIVERSARY);
|
||||
operationList.add(builder.build());
|
||||
}
|
||||
|
||||
if (mAndroidCustomPropertyList != null) {
|
||||
for (List<String> customPropertyList : mAndroidCustomPropertyList) {
|
||||
int size = customPropertyList.size();
|
||||
@@ -1322,7 +1366,7 @@ public class VCardEntry {
|
||||
&& TextUtils.isEmpty(mGivenName)
|
||||
&& TextUtils.isEmpty(mPrefix)
|
||||
&& TextUtils.isEmpty(mSuffix)
|
||||
&& TextUtils.isEmpty(mFullName)
|
||||
&& TextUtils.isEmpty(mFormattedName)
|
||||
&& TextUtils.isEmpty(mPhoneticFamilyName)
|
||||
&& TextUtils.isEmpty(mPhoneticMiddleName)
|
||||
&& TextUtils.isEmpty(mPhoneticGivenName)
|
||||
@@ -1381,7 +1425,7 @@ public class VCardEntry {
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return mFullName;
|
||||
return mFormattedName;
|
||||
}
|
||||
|
||||
public String getPhoneticFamilyName() {
|
||||
|
||||
@@ -52,9 +52,9 @@ public class VCardEntryCommitter implements VCardEntryHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryCreated(final VCardEntry contactStruct) {
|
||||
public void onEntryCreated(final VCardEntry vcardEntry) {
|
||||
long start = System.currentTimeMillis();
|
||||
mCreatedUris.add(contactStruct.pushIntoContentResolver(mContentResolver));
|
||||
mCreatedUris.add(vcardEntry.pushIntoContentResolver(mContentResolver));
|
||||
mTimeToCommit += System.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,78 +16,82 @@
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.net.QuotedPrintableCodec;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects
|
||||
* to easily handle each vCard entry.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class understand details inside vCard and translates it to {@link VCardEntry}.
|
||||
* Then the class throw it to {@link VCardEntryHandler} registered via
|
||||
* {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects
|
||||
* are able to handle the {@link VCardEntry} object.
|
||||
* </p>
|
||||
* <p>
|
||||
* If you want to know the detail inside vCard, it would be better to implement
|
||||
* {@link VCardInterpreter} directly, instead of relying on this class and
|
||||
* {@link VCardEntry} created by the object.
|
||||
* </p>
|
||||
*/
|
||||
public class VCardEntryConstructor implements VCardInterpreter {
|
||||
private static String LOG_TAG = "VCardEntryConstructor";
|
||||
|
||||
/**
|
||||
* If there's no other information available, this class uses this charset for encoding
|
||||
* byte arrays to String.
|
||||
*/
|
||||
/* package */ static final String DEFAULT_CHARSET_FOR_DECODED_BYTES = "UTF-8";
|
||||
|
||||
private VCardEntry.Property mCurrentProperty = new VCardEntry.Property();
|
||||
private VCardEntry mCurrentContactStruct;
|
||||
private VCardEntry mCurrentVCardEntry;
|
||||
private String mParamType;
|
||||
|
||||
/**
|
||||
* The charset using which {@link VCardInterpreter} parses the text.
|
||||
*/
|
||||
private String mInputCharset;
|
||||
// The charset using which {@link VCardInterpreter} parses the text.
|
||||
// Each String is first decoded into binary stream with this charset, and encoded back
|
||||
// to "target charset", which may be explicitly specified by the vCard with "CHARSET"
|
||||
// property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8).
|
||||
private final String mSourceCharset;
|
||||
|
||||
/**
|
||||
* The charset with which byte array is encoded to String.
|
||||
*/
|
||||
final private String mCharsetForDecodedBytes;
|
||||
final private boolean mStrictLineBreakParsing;
|
||||
final private int mVCardType;
|
||||
final private Account mAccount;
|
||||
private final boolean mStrictLineBreaking;
|
||||
private final int mVCardType;
|
||||
private final Account mAccount;
|
||||
|
||||
/** For measuring performance. */
|
||||
// For measuring performance.
|
||||
private long mTimePushIntoContentResolver;
|
||||
|
||||
final private List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
|
||||
private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
|
||||
|
||||
public VCardEntryConstructor() {
|
||||
this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, null);
|
||||
this(VCardConfig.VCARD_TYPE_V21_GENERIC, null);
|
||||
}
|
||||
|
||||
public VCardEntryConstructor(final int vcardType) {
|
||||
this(null, null, false, vcardType, null);
|
||||
this(vcardType, null, null, false);
|
||||
}
|
||||
|
||||
public VCardEntryConstructor(final String charset, final boolean strictLineBreakParsing,
|
||||
final int vcardType, final Account account) {
|
||||
this(null, charset, strictLineBreakParsing, vcardType, account);
|
||||
public VCardEntryConstructor(final int vcardType, final Account account) {
|
||||
this(vcardType, account, null, false);
|
||||
}
|
||||
|
||||
public VCardEntryConstructor(final String inputCharset, final String charsetForDetodedBytes,
|
||||
final boolean strictLineBreakParsing, final int vcardType,
|
||||
final Account account) {
|
||||
public VCardEntryConstructor(final int vcardType, final Account account,
|
||||
final String inputCharset) {
|
||||
this(vcardType, account, inputCharset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide Just for testing.
|
||||
*/
|
||||
public VCardEntryConstructor(final int vcardType, final Account account,
|
||||
final String inputCharset, final boolean strictLineBreakParsing) {
|
||||
if (inputCharset != null) {
|
||||
mInputCharset = inputCharset;
|
||||
mSourceCharset = inputCharset;
|
||||
} else {
|
||||
mInputCharset = VCardConfig.DEFAULT_CHARSET;
|
||||
mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
|
||||
}
|
||||
if (charsetForDetodedBytes != null) {
|
||||
mCharsetForDecodedBytes = charsetForDetodedBytes;
|
||||
} else {
|
||||
mCharsetForDecodedBytes = DEFAULT_CHARSET_FOR_DECODED_BYTES;
|
||||
}
|
||||
mStrictLineBreakParsing = strictLineBreakParsing;
|
||||
mStrictLineBreaking = strictLineBreakParsing;
|
||||
mVCardType = vcardType;
|
||||
mAccount = account;
|
||||
}
|
||||
@@ -95,60 +99,63 @@ public class VCardEntryConstructor implements VCardInterpreter {
|
||||
public void addEntryHandler(VCardEntryHandler entryHandler) {
|
||||
mEntryHandlers.add(entryHandler);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||
entryHandler.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||
entryHandler.onEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the parse failed between {@link #startEntry()} and {@link #endEntry()}.
|
||||
*/
|
||||
public void clear() {
|
||||
mCurrentContactStruct = null;
|
||||
mCurrentVCardEntry = null;
|
||||
mCurrentProperty = new VCardEntry.Property();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assume that VCard is not nested. In other words, this code does not accept
|
||||
*/
|
||||
@Override
|
||||
public void startEntry() {
|
||||
if (mCurrentContactStruct != null) {
|
||||
if (mCurrentVCardEntry != null) {
|
||||
Log.e(LOG_TAG, "Nested VCard code is not supported now.");
|
||||
}
|
||||
mCurrentContactStruct = new VCardEntry(mVCardType, mAccount);
|
||||
mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endEntry() {
|
||||
mCurrentContactStruct.consolidateFields();
|
||||
mCurrentVCardEntry.consolidateFields();
|
||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||
entryHandler.onEntryCreated(mCurrentContactStruct);
|
||||
entryHandler.onEntryCreated(mCurrentVCardEntry);
|
||||
}
|
||||
mCurrentContactStruct = null;
|
||||
mCurrentVCardEntry = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startProperty() {
|
||||
mCurrentProperty.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endProperty() {
|
||||
mCurrentContactStruct.addProperty(mCurrentProperty);
|
||||
mCurrentVCardEntry.addProperty(mCurrentProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyName(String name) {
|
||||
mCurrentProperty.setPropertyName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyGroup(String group) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyParamType(String type) {
|
||||
if (mParamType != null) {
|
||||
Log.e(LOG_TAG, "propertyParamType() is called more than once " +
|
||||
@@ -164,119 +171,34 @@ public class VCardEntryConstructor implements VCardInterpreter {
|
||||
mParamType = "TYPE";
|
||||
}
|
||||
if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
|
||||
value = encodeString(value, mCharsetForDecodedBytes);
|
||||
value = VCardUtils.convertStringCharset(
|
||||
value, mSourceCharset, VCardConfig.DEFAULT_IMPORT_CHARSET);
|
||||
}
|
||||
mCurrentProperty.addParameter(mParamType, value);
|
||||
mParamType = null;
|
||||
}
|
||||
|
||||
private String encodeString(String originalString, String charsetForDecodedBytes) {
|
||||
if (mInputCharset.equalsIgnoreCase(charsetForDecodedBytes)) {
|
||||
return originalString;
|
||||
private String handleOneValue(String value,
|
||||
String sourceCharset, String targetCharset, String encoding) {
|
||||
// It is possible when some of multiple values are empty.
|
||||
// e.g. N:;a;;; -> values are "", "a", "", "", and "".
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
return "";
|
||||
}
|
||||
Charset charset = Charset.forName(mInputCharset);
|
||||
ByteBuffer byteBuffer = charset.encode(originalString);
|
||||
// byteBuffer.array() "may" return byte array which is larger than
|
||||
// byteBuffer.remaining(). Here, we keep on the safe side.
|
||||
byte[] bytes = new byte[byteBuffer.remaining()];
|
||||
byteBuffer.get(bytes);
|
||||
try {
|
||||
return new String(bytes, charsetForDecodedBytes);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + charsetForDecodedBytes);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String handleOneValue(String value, String charsetForDecodedBytes, String encoding) {
|
||||
if (encoding != null) {
|
||||
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
||||
mCurrentProperty.setPropertyBytes(Base64.decodeBase64(value.getBytes()));
|
||||
mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT));
|
||||
return value;
|
||||
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
||||
// "= " -> " ", "=\t" -> "\t".
|
||||
// Previous code had done this replacement. Keep on the safe side.
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = value.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = value.charAt(i);
|
||||
if (ch == '=' && i < length - 1) {
|
||||
char nextCh = value.charAt(i + 1);
|
||||
if (nextCh == ' ' || nextCh == '\t') {
|
||||
|
||||
builder.append(nextCh);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
builder.append(ch);
|
||||
}
|
||||
String quotedPrintable = builder.toString();
|
||||
|
||||
String[] lines;
|
||||
if (mStrictLineBreakParsing) {
|
||||
lines = quotedPrintable.split("\r\n");
|
||||
} else {
|
||||
builder = new StringBuilder();
|
||||
length = quotedPrintable.length();
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = quotedPrintable.charAt(i);
|
||||
if (ch == '\n') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
} else if (ch == '\r') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
if (i < length - 1) {
|
||||
char nextCh = quotedPrintable.charAt(i + 1);
|
||||
if (nextCh == '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
String finalLine = builder.toString();
|
||||
if (finalLine.length() > 0) {
|
||||
list.add(finalLine);
|
||||
}
|
||||
lines = list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
builder = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
if (line.endsWith("=")) {
|
||||
line = line.substring(0, line.length() - 1);
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = builder.toString().getBytes(mInputCharset);
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + mInputCharset);
|
||||
bytes = builder.toString().getBytes();
|
||||
}
|
||||
|
||||
try {
|
||||
bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
|
||||
} catch (DecoderException e) {
|
||||
Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return new String(bytes, charsetForDecodedBytes);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + charsetForDecodedBytes);
|
||||
return new String(bytes);
|
||||
}
|
||||
return VCardUtils.parseQuotedPrintable(
|
||||
value, mStrictLineBreaking, sourceCharset, targetCharset);
|
||||
}
|
||||
// Unknown encoding. Fall back to default.
|
||||
Log.w(LOG_TAG, "Unknown encoding. Fall back to default.");
|
||||
}
|
||||
return encodeString(value, charsetForDecodedBytes);
|
||||
|
||||
// Just translate the charset of a given String from inputCharset to a system one.
|
||||
return VCardUtils.convertStringCharset(value, sourceCharset, targetCharset);
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
@@ -284,24 +206,27 @@ public class VCardEntryConstructor implements VCardInterpreter {
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET");
|
||||
final String charset =
|
||||
((charsetCollection != null) ? charsetCollection.iterator().next() : null);
|
||||
final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING");
|
||||
final Collection<String> charsetCollection =
|
||||
mCurrentProperty.getParameters(VCardConstants.PARAM_CHARSET);
|
||||
final Collection<String> encodingCollection =
|
||||
mCurrentProperty.getParameters(VCardConstants.PARAM_ENCODING);
|
||||
final String encoding =
|
||||
((encodingCollection != null) ? encodingCollection.iterator().next() : null);
|
||||
|
||||
String charsetForDecodedBytes = CharsetUtils.nameForDefaultVendor(charset);
|
||||
if (charsetForDecodedBytes == null || charsetForDecodedBytes.length() == 0) {
|
||||
charsetForDecodedBytes = mCharsetForDecodedBytes;
|
||||
String targetCharset = CharsetUtils.nameForDefaultVendor(
|
||||
((charsetCollection != null) ? charsetCollection.iterator().next() : null));
|
||||
if (TextUtils.isEmpty(targetCharset)) {
|
||||
targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
|
||||
}
|
||||
|
||||
for (final String value : values) {
|
||||
mCurrentProperty.addToPropertyValueList(
|
||||
handleOneValue(value, charsetForDecodedBytes, encoding));
|
||||
handleOneValue(value, mSourceCharset, targetCharset, encoding));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void showPerformanceInfo() {
|
||||
Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
|
||||
mTimePushIntoContentResolver + " ms");
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
package android.pim.vcard;
|
||||
|
||||
/**
|
||||
* The interface called by {@link VCardEntryConstructor}. Useful when you don't want to
|
||||
* handle detailed information as what {@link VCardParser} provides via {@link VCardInterpreter}.
|
||||
* <p>
|
||||
* The interface called by {@link VCardEntryConstructor}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class is useful when you don't want to know vCard data in detail. If you want to know
|
||||
* it, it would be better to consider using {@link VCardInterpreter}.
|
||||
* </p>
|
||||
*/
|
||||
public interface VCardEntryHandler {
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.List;
|
||||
/**
|
||||
* <P>
|
||||
* The interface which should be implemented by the classes which have to analyze each
|
||||
* vCard entry more minutely than {@link VCardEntry} class analysis.
|
||||
* vCard entry minutely.
|
||||
* </P>
|
||||
* <P>
|
||||
* Here, there are several terms specific to vCard (and this library).
|
||||
|
||||
@@ -23,9 +23,9 @@ import java.util.List;
|
||||
* {@link VCardInterpreter} objects and make a user object treat them as one
|
||||
* {@link VCardInterpreter} object.
|
||||
*/
|
||||
public class VCardInterpreterCollection implements VCardInterpreter {
|
||||
public final class VCardInterpreterCollection implements VCardInterpreter {
|
||||
private final Collection<VCardInterpreter> mInterpreterCollection;
|
||||
|
||||
|
||||
public VCardInterpreterCollection(Collection<VCardInterpreter> interpreterCollection) {
|
||||
mInterpreterCollection = interpreterCollection;
|
||||
}
|
||||
|
||||
@@ -20,82 +20,36 @@ import android.pim.vcard.exception.VCardException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public abstract class VCardParser {
|
||||
protected final int mParseType;
|
||||
protected boolean mCanceled;
|
||||
|
||||
public VCardParser() {
|
||||
this(VCardConfig.PARSE_TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
public VCardParser(int parseType) {
|
||||
mParseType = parseType;
|
||||
}
|
||||
|
||||
public interface VCardParser {
|
||||
/**
|
||||
* <P>
|
||||
* Parses the given stream and send the VCard data into VCardBuilderBase object.
|
||||
* </P.
|
||||
* <P>
|
||||
* <p>
|
||||
* Parses the given stream and send the vCard data into VCardBuilderBase object.
|
||||
* </p>.
|
||||
* <p>
|
||||
* Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
|
||||
* local encoding to it. For example, Japanese phone career uses Shift_JIS, which is
|
||||
* formally allowed in VCard 2.1, but not recommended in VCard 3.0. In VCard 2.1,
|
||||
* In some exreme case, some VCard may have different charsets in one VCard (though
|
||||
* we do not see any device which emits such kind of malicious data)
|
||||
* </P>
|
||||
* <P>
|
||||
* In order to avoid "misunderstanding" charset as much as possible, this method
|
||||
* use "ISO-8859-1" for reading the stream. When charset is specified in some property
|
||||
* (with "CHARSET=..." parameter), the string is decoded to raw bytes and encoded to
|
||||
* the charset. This method assumes that "ISO-8859-1" has 1 to 1 mapping in all 8bit
|
||||
* characters, which is not completely sure. In some cases, this "decoding-encoding"
|
||||
* scheme may fail. To avoid the case,
|
||||
* </P>
|
||||
* <P>
|
||||
* We recommend you to use {@link VCardSourceDetector} and detect which kind of source the
|
||||
* VCard comes from and explicitly specify a charset using the result.
|
||||
* </P>
|
||||
* formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1,
|
||||
* In some exreme case, it is allowed for vCard to have different charsets in one vCard.
|
||||
* </p>
|
||||
* <p>
|
||||
* We recommend you use {@link VCardSourceDetector} and detect which kind of source the
|
||||
* vCard comes from and explicitly specify a charset using the result.
|
||||
* </p>
|
||||
*
|
||||
* @param is The source to parse.
|
||||
* @param interepreter A {@link VCardInterpreter} object which used to construct data.
|
||||
* @return Returns true for success. Otherwise returns false.
|
||||
* @throws IOException, VCardException
|
||||
*/
|
||||
public abstract boolean parse(InputStream is, VCardInterpreter interepreter)
|
||||
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException;
|
||||
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* The method variants which accept charset.
|
||||
* </P>
|
||||
* <P>
|
||||
* RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use
|
||||
* UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese
|
||||
* phone uses Shift_JIS as a charset (e.g. W61SH), and another uses
|
||||
* "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification (e.g. W53K).
|
||||
* </P>
|
||||
*
|
||||
* @param is The source to parse.
|
||||
* @param charset Charset to be used.
|
||||
* @param builder The VCardBuilderBase object.
|
||||
* @return Returns true when successful. Otherwise returns false.
|
||||
* @throws IOException, VCardException
|
||||
* <p>
|
||||
* Cancel parsing vCard. Useful when you want to stop the parse in the other threads.
|
||||
* </p>
|
||||
* <p>
|
||||
* Actual cancel is done after parsing the current vcard.
|
||||
* </p>
|
||||
*/
|
||||
public abstract boolean parse(InputStream is, String charset, VCardInterpreter builder)
|
||||
throws IOException, VCardException;
|
||||
|
||||
/**
|
||||
* The method variants which tells this object the operation is already canceled.
|
||||
*/
|
||||
public abstract void parse(InputStream is, String charset,
|
||||
VCardInterpreter builder, boolean canceled)
|
||||
throws IOException, VCardException;
|
||||
|
||||
/**
|
||||
* Cancel parsing.
|
||||
* Actual cancel is done after the end of the current one vcard entry parsing.
|
||||
*/
|
||||
public void cancel() {
|
||||
mCanceled = true;
|
||||
}
|
||||
public abstract void cancel();
|
||||
}
|
||||
|
||||
1046
core/java/android/pim/vcard/VCardParserImpl_V21.java
Normal file
1046
core/java/android/pim/vcard/VCardParserImpl_V21.java
Normal file
File diff suppressed because it is too large
Load Diff
388
core/java/android/pim/vcard/VCardParserImpl_V30.java
Normal file
388
core/java/android/pim/vcard/VCardParserImpl_V30.java
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Basic implementation achieving vCard 3.0 parsing.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class inherits vCard 2.1 implementation since technically they are similar,
|
||||
* while specifically there's logical no relevance between them.
|
||||
* So that developers are not confused with the inheritance,
|
||||
* {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while
|
||||
* {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}.
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
/* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 {
|
||||
private static final String LOG_TAG = "VCardParserImpl_V30";
|
||||
|
||||
private String mPreviousLine;
|
||||
private boolean mEmittedAgentWarning = false;
|
||||
|
||||
public VCardParserImpl_V30() {
|
||||
super();
|
||||
}
|
||||
|
||||
public VCardParserImpl_V30(int vcardType) {
|
||||
super(vcardType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return VCardConfig.VERSION_30;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVersionString() {
|
||||
return VCardConstants.VERSION_V30;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLine() throws IOException {
|
||||
if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
} else {
|
||||
return mReader.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 requires that the line with space at the beginning of the line
|
||||
* must be combined with previous line.
|
||||
*/
|
||||
@Override
|
||||
protected String getNonEmptyLine() throws IOException, VCardException {
|
||||
String line;
|
||||
StringBuilder builder = null;
|
||||
while (true) {
|
||||
line = mReader.readLine();
|
||||
if (line == null) {
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
} else if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
}
|
||||
throw new VCardException("Reached end of buffer.");
|
||||
} else if (line.length() == 0) {
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
} else if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
}
|
||||
} else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
|
||||
if (builder != null) {
|
||||
// See Section 5.8.1 of RFC 2425 (MIME-DIR document).
|
||||
// Following is the excerpts from it.
|
||||
//
|
||||
// DESCRIPTION:This is a long description that exists on a long line.
|
||||
//
|
||||
// Can be represented as:
|
||||
//
|
||||
// DESCRIPTION:This is a long description
|
||||
// that exists on a long line.
|
||||
//
|
||||
// It could also be represented as:
|
||||
//
|
||||
// DESCRIPTION:This is a long descrip
|
||||
// tion that exists o
|
||||
// n a long line.
|
||||
builder.append(line.substring(1));
|
||||
} else if (mPreviousLine != null) {
|
||||
builder = new StringBuilder();
|
||||
builder.append(mPreviousLine);
|
||||
mPreviousLine = null;
|
||||
builder.append(line.substring(1));
|
||||
} else {
|
||||
throw new VCardException("Space exists at the beginning of the line");
|
||||
}
|
||||
} else {
|
||||
if (mPreviousLine == null) {
|
||||
mPreviousLine = line;
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
}
|
||||
} else {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = line;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF
|
||||
* 1 * (contentline)
|
||||
* ;A vCard object MUST include the VERSION, FN and N types.
|
||||
* [group "."] "END" ":" "VCARD" 1 * CRLF
|
||||
*/
|
||||
@Override
|
||||
protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
|
||||
// TODO: vCard 3.0 supports group.
|
||||
return super.readBeginVCard(allowGarbage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readEndVCard(boolean useCache, boolean allowGarbage)
|
||||
throws IOException, VCardException {
|
||||
// TODO: vCard 3.0 supports group.
|
||||
super.readEndVCard(useCache, allowGarbage);
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
|
||||
*/
|
||||
@Override
|
||||
protected void handleParams(final String params) throws VCardException {
|
||||
try {
|
||||
super.handleParams(params);
|
||||
} catch (VCardException e) {
|
||||
// maybe IANA type
|
||||
String[] strArray = params.split("=", 2);
|
||||
if (strArray.length == 2) {
|
||||
handleAnyParam(strArray[0], strArray[1]);
|
||||
} else {
|
||||
// Must not come here in the current implementation.
|
||||
throw new VCardException(
|
||||
"Unknown params value: " + params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAnyParam(final String paramName, final String paramValue) {
|
||||
mInterpreter.propertyParamType(paramName);
|
||||
splitAndPutParamValue(paramValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleParamWithoutName(final String paramValue) {
|
||||
handleType(paramValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* vCard 3.0 defines
|
||||
*
|
||||
* param = param-name "=" param-value *("," param-value)
|
||||
* param-name = iana-token / x-name
|
||||
* param-value = ptext / quoted-string
|
||||
* quoted-string = DQUOTE QSAFE-CHAR DQUOTE
|
||||
* QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-ASCII
|
||||
* ; Any character except CTLs, DQUOTE
|
||||
*
|
||||
* QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
|
||||
*/
|
||||
@Override
|
||||
protected void handleType(final String paramValue) {
|
||||
mInterpreter.propertyParamType("TYPE");
|
||||
splitAndPutParamValue(paramValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits parameter values into pieces in accordance with vCard 3.0 specification and
|
||||
* puts pieces into mInterpreter.
|
||||
*/
|
||||
/*
|
||||
* param-value = ptext / quoted-string
|
||||
* quoted-string = DQUOTE QSAFE-CHAR DQUOTE
|
||||
* QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-ASCII
|
||||
* ; Any character except CTLs, DQUOTE
|
||||
*
|
||||
* QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
|
||||
*/
|
||||
private void splitAndPutParamValue(String paramValue) {
|
||||
// "comma,separated:inside.dquote",pref
|
||||
// -->
|
||||
// - comma,separated:inside.dquote
|
||||
// - pref
|
||||
//
|
||||
// Note: Though there's a code, we don't need to take much care of
|
||||
// wrongly-added quotes like the example above, as they induce
|
||||
// parse errors at the top level (when splitting a line into parts).
|
||||
StringBuilder builder = null; // Delay initialization.
|
||||
boolean insideDquote = false;
|
||||
final int length = paramValue.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
final char ch = paramValue.charAt(i);
|
||||
if (ch == '"') {
|
||||
if (insideDquote) {
|
||||
// End of Dquote.
|
||||
mInterpreter.propertyParamValue(builder.toString());
|
||||
builder = null;
|
||||
insideDquote = false;
|
||||
} else {
|
||||
if (builder != null) {
|
||||
if (builder.length() > 0) {
|
||||
// e.g.
|
||||
// pref"quoted"
|
||||
Log.w(LOG_TAG, "Unexpected Dquote inside property.");
|
||||
} else {
|
||||
// e.g.
|
||||
// pref,"quoted"
|
||||
// "quoted",pref
|
||||
mInterpreter.propertyParamValue(builder.toString());
|
||||
}
|
||||
}
|
||||
insideDquote = true;
|
||||
}
|
||||
} else if (ch == ',' && !insideDquote) {
|
||||
if (builder == null) {
|
||||
Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
|
||||
paramValue + ")");
|
||||
} else {
|
||||
mInterpreter.propertyParamValue(builder.toString());
|
||||
builder = null;
|
||||
}
|
||||
} else {
|
||||
// To stop creating empty StringBuffer at the end of parameter,
|
||||
// we delay creating this object until this point.
|
||||
if (builder == null) {
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
if (insideDquote) {
|
||||
// e.g.
|
||||
// "non-quote-at-end
|
||||
Log.d(LOG_TAG, "Dangling Dquote.");
|
||||
}
|
||||
if (builder != null) {
|
||||
if (builder.length() == 0) {
|
||||
Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
|
||||
"at the end of parameter value parsing.");
|
||||
} else {
|
||||
mInterpreter.propertyParamValue(builder.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAgent(final String propertyValue) {
|
||||
// The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
|
||||
//
|
||||
// e.g.
|
||||
// AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
|
||||
// TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
|
||||
// ET:jfriday@host.com\nEND:VCARD\n
|
||||
//
|
||||
// TODO: fix this.
|
||||
//
|
||||
// issue:
|
||||
// vCard 3.0 also allows this as an example.
|
||||
//
|
||||
// AGENT;VALUE=uri:
|
||||
// CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
|
||||
//
|
||||
// This is not vCard. Should we support this?
|
||||
//
|
||||
// Just ignore the line for now, since we cannot know how to handle it...
|
||||
if (!mEmittedAgentWarning) {
|
||||
Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
|
||||
mEmittedAgentWarning = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 does not require two CRLF at the last of BASE64 data.
|
||||
* It only requires that data should be MIME-encoded.
|
||||
*/
|
||||
@Override
|
||||
protected String getBase64(final String firstString)
|
||||
throws IOException, VCardException {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString);
|
||||
|
||||
while (true) {
|
||||
final String line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("File ended during parsing BASE64 binary");
|
||||
}
|
||||
if (line.length() == 0) {
|
||||
break;
|
||||
} else if (!line.startsWith(" ") && !line.startsWith("\t")) {
|
||||
mPreviousLine = line;
|
||||
break;
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
|
||||
* ; \\ encodes \, \n or \N encodes newline
|
||||
* ; \; encodes ;, \, encodes ,
|
||||
*
|
||||
* Note: Apple escapes ':' into '\:' while does not escape '\'
|
||||
*/
|
||||
@Override
|
||||
protected String maybeUnescapeText(final String text) {
|
||||
return unescapeText(text);
|
||||
}
|
||||
|
||||
public static String unescapeText(final String text) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
final int length = text.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch == '\\' && i < length - 1) {
|
||||
final char next_ch = text.charAt(++i);
|
||||
if (next_ch == 'n' || next_ch == 'N') {
|
||||
builder.append("\n");
|
||||
} else {
|
||||
builder.append(next_ch);
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String maybeUnescapeCharacter(final char ch) {
|
||||
return unescapeCharacter(ch);
|
||||
}
|
||||
|
||||
public static String unescapeCharacter(final char ch) {
|
||||
if (ch == 'n' || ch == 'N') {
|
||||
return "\n";
|
||||
} else {
|
||||
return String.valueOf(ch);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> getKnownPropertyNameSet() {
|
||||
return VCardParser_V30.sKnownPropertyNameSet;
|
||||
}
|
||||
}
|
||||
92
core/java/android/pim/vcard/VCardParserImpl_V40.java
Normal file
92
core/java/android/pim/vcard/VCardParserImpl_V40.java
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Basic implementation parsing vCard 4.0.
|
||||
* </p>
|
||||
* <p>
|
||||
* vCard 4.0 is not published yet. Also this implementation is premature.
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
/* package */ class VCardParserImpl_V40 extends VCardParserImpl_V30 {
|
||||
// private static final String LOG_TAG = "VCardParserImpl_V40";
|
||||
|
||||
public VCardParserImpl_V40() {
|
||||
super();
|
||||
}
|
||||
|
||||
public VCardParserImpl_V40(final int vcardType) {
|
||||
super(vcardType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return VCardConfig.VERSION_40;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVersionString() {
|
||||
return VCardConstants.VERSION_V40;
|
||||
}
|
||||
|
||||
/**
|
||||
* We escape "\N" into new line for safety.
|
||||
*/
|
||||
@Override
|
||||
protected String maybeUnescapeText(final String text) {
|
||||
return unescapeText(text);
|
||||
}
|
||||
|
||||
public static String unescapeText(final String text) {
|
||||
// TODO: more strictly, vCard 4.0 requires different type of unescaping rule
|
||||
// toward each property.
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final int length = text.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch == '\\' && i < length - 1) {
|
||||
final char next_ch = text.charAt(++i);
|
||||
if (next_ch == 'n' || next_ch == 'N') {
|
||||
builder.append("\n");
|
||||
} else {
|
||||
builder.append(next_ch);
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String unescapeCharacter(final char ch) {
|
||||
if (ch == 'n' || ch == 'N') {
|
||||
return "\n";
|
||||
} else {
|
||||
return String.valueOf(ch);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> getKnownPropertyNameSet() {
|
||||
return VCardParser_V40.sKnownPropertyNameSet;
|
||||
}
|
||||
}
|
||||
@@ -15,922 +15,95 @@
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.pim.vcard.exception.VCardAgentNotSupportedException;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.pim.vcard.exception.VCardInvalidCommentLineException;
|
||||
import android.pim.vcard.exception.VCardInvalidLineException;
|
||||
import android.pim.vcard.exception.VCardNestedException;
|
||||
import android.pim.vcard.exception.VCardVersionException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is used to parse vCard. Please refer to vCard Specification 2.1 for more detail.
|
||||
* </p>
|
||||
* vCard parser for vCard 2.1. See the specification for more detail about the spec itself.
|
||||
* </p>
|
||||
* <p>
|
||||
* The spec is written in 1996, and currently various types of "vCard 2.1" exist.
|
||||
* To handle real the world vCard formats appropriately and effectively, this class does not
|
||||
* obey with strict vCard 2.1.
|
||||
* In stead, not only vCard spec but also real world vCard is considered.
|
||||
* </p>
|
||||
* e.g. A lot of devices and softwares let vCard importer/exporter to use
|
||||
* the PNG format to determine the type of image, while it is not allowed in
|
||||
* the original specification. As of 2010, we can see even the FLV format
|
||||
* (possible in Japanese mobile phones).
|
||||
* </p>
|
||||
*/
|
||||
public class VCardParser_V21 extends VCardParser {
|
||||
private static final String LOG_TAG = "VCardParser_V21";
|
||||
|
||||
/** Store the known-type */
|
||||
private static final HashSet<String> sKnownTypeSet = new HashSet<String>(
|
||||
Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
|
||||
"PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
|
||||
"MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
|
||||
"ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
|
||||
"MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF",
|
||||
"CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF",
|
||||
"PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI",
|
||||
"WAVE", "AIFF", "PCM", "X509", "PGP"));
|
||||
|
||||
/** Store the known-value */
|
||||
private static final HashSet<String> sKnownValueSet = new HashSet<String>(
|
||||
Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"));
|
||||
|
||||
/** Store the property names available in vCard 2.1 */
|
||||
private static final HashSet<String> sAvailablePropertyNameSetV21 =
|
||||
new HashSet<String>(Arrays.asList(
|
||||
"BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
||||
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
||||
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER"));
|
||||
public final class VCardParser_V21 implements VCardParser {
|
||||
/**
|
||||
* A unmodifiable Set storing the property names available in the vCard 2.1 specification.
|
||||
*/
|
||||
/* package */ static final Set<String> sKnownPropertyNameSet =
|
||||
Collections.unmodifiableSet(new HashSet<String>(
|
||||
Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
||||
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
||||
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER")));
|
||||
|
||||
/**
|
||||
* A unmodifiable Set storing the types known in vCard 2.1.
|
||||
*/
|
||||
/* package */ static final Set<String> sKnownTypeSet =
|
||||
Collections.unmodifiableSet(new HashSet<String>(
|
||||
Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
|
||||
"PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
|
||||
"MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
|
||||
"ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
|
||||
"MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF",
|
||||
"CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF",
|
||||
"PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI",
|
||||
"WAVE", "AIFF", "PCM", "X509", "PGP")));
|
||||
|
||||
/**
|
||||
* A unmodifiable Set storing the values for the type "VALUE", available in the vCard 2.1.
|
||||
*/
|
||||
/* package */ static final Set<String> sKnownValueSet =
|
||||
Collections.unmodifiableSet(new HashSet<String>(
|
||||
Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID")));
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1.
|
||||
* </p>
|
||||
* <p>
|
||||
* Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
|
||||
* We allow it for safety...
|
||||
* We allow it for safety.
|
||||
* </p>
|
||||
*/
|
||||
private static final HashSet<String> sAvailableEncodingV21 =
|
||||
new HashSet<String>(Arrays.asList(
|
||||
"7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
|
||||
|
||||
// Used only for parsing END:VCARD.
|
||||
private String mPreviousLine;
|
||||
|
||||
/** The builder to build parsed data */
|
||||
protected VCardInterpreter mBuilder = null;
|
||||
/* package */ static final Set<String> sAvailableEncoding =
|
||||
Collections.unmodifiableSet(new HashSet<String>(
|
||||
Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT,
|
||||
VCardConstants.PARAM_ENCODING_8BIT,
|
||||
VCardConstants.PARAM_ENCODING_QP,
|
||||
VCardConstants.PARAM_ENCODING_BASE64,
|
||||
VCardConstants.PARAM_ENCODING_B)));
|
||||
|
||||
/**
|
||||
* The encoding type. "Encoding" in vCard is different from "Charset".
|
||||
* e.g. 7BIT, 8BIT, QUOTED-PRINTABLE.
|
||||
*/
|
||||
protected String mEncoding = null;
|
||||
|
||||
protected final String sDefaultEncoding = "8BIT";
|
||||
|
||||
// Should not directly read a line from this object. Use getLine() instead.
|
||||
protected BufferedReader mReader;
|
||||
|
||||
// In some cases, vCard is nested. Currently, we only consider the most interior vCard data.
|
||||
// See v21_foma_1.vcf in test directory for more information.
|
||||
private int mNestCount;
|
||||
|
||||
// In order to reduce warning message as much as possible, we hold the value which made Logger
|
||||
// emit a warning message.
|
||||
protected Set<String> mUnknownTypeMap = new HashSet<String>();
|
||||
protected Set<String> mUnknownValueMap = new HashSet<String>();
|
||||
|
||||
// For measuring performance.
|
||||
private long mTimeTotal;
|
||||
private long mTimeReadStartRecord;
|
||||
private long mTimeReadEndRecord;
|
||||
private long mTimeStartProperty;
|
||||
private long mTimeEndProperty;
|
||||
private long mTimeParseItems;
|
||||
private long mTimeParseLineAndHandleGroup;
|
||||
private long mTimeParsePropertyValues;
|
||||
private long mTimeParseAdrOrgN;
|
||||
private long mTimeHandleMiscPropertyValue;
|
||||
private long mTimeHandleQuotedPrintable;
|
||||
private long mTimeHandleBase64;
|
||||
private final VCardParserImpl_V21 mVCardParserImpl;
|
||||
|
||||
public VCardParser_V21() {
|
||||
this(null);
|
||||
mVCardParserImpl = new VCardParserImpl_V21();
|
||||
}
|
||||
|
||||
public VCardParser_V21(VCardSourceDetector detector) {
|
||||
this(detector != null ? detector.getEstimatedType() : VCardConfig.PARSE_TYPE_UNKNOWN);
|
||||
public VCardParser_V21(int vcardType) {
|
||||
mVCardParserImpl = new VCardParserImpl_V21(vcardType);
|
||||
}
|
||||
|
||||
public VCardParser_V21(int parseType) {
|
||||
super(parseType);
|
||||
if (parseType == VCardConfig.PARSE_TYPE_FOMA) {
|
||||
mNestCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the file at the given position.
|
||||
*
|
||||
* vcard_file = [wsls] vcard [wsls]
|
||||
*/
|
||||
protected void parseVCardFile() throws IOException, VCardException {
|
||||
boolean firstReading = true;
|
||||
while (true) {
|
||||
if (mCanceled) {
|
||||
break;
|
||||
}
|
||||
if (!parseOneVCard(firstReading)) {
|
||||
break;
|
||||
}
|
||||
firstReading = false;
|
||||
}
|
||||
|
||||
if (mNestCount > 0) {
|
||||
boolean useCache = true;
|
||||
for (int i = 0; i < mNestCount; i++) {
|
||||
readEndVCard(useCache, true);
|
||||
useCache = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int getVersion() {
|
||||
return VCardConfig.FLAG_V21;
|
||||
}
|
||||
|
||||
protected String getVersionString() {
|
||||
return VCardConstants.VERSION_V21;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when the propertyName is a valid property name.
|
||||
*/
|
||||
protected boolean isValidPropertyName(String propertyName) {
|
||||
if (!(sAvailablePropertyNameSetV21.contains(propertyName.toUpperCase()) ||
|
||||
propertyName.startsWith("X-")) &&
|
||||
!mUnknownTypeMap.contains(propertyName)) {
|
||||
mUnknownTypeMap.add(propertyName);
|
||||
Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when the encoding is a valid encoding.
|
||||
*/
|
||||
protected boolean isValidEncoding(String encoding) {
|
||||
return sAvailableEncodingV21.contains(encoding.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String. It may be null, or its length may be 0
|
||||
* @throws IOException
|
||||
*/
|
||||
protected String getLine() throws IOException {
|
||||
return mReader.readLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String with it's length > 0
|
||||
* @throws IOException
|
||||
* @throws VCardException when the stream reached end of line
|
||||
*/
|
||||
protected String getNonEmptyLine() throws IOException, VCardException {
|
||||
String line;
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("Reached end of buffer.");
|
||||
} else if (line.trim().length() > 0) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
|
||||
* items *CRLF
|
||||
* "END" [ws] ":" [ws] "VCARD"
|
||||
*/
|
||||
private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException {
|
||||
boolean allowGarbage = false;
|
||||
if (firstReading) {
|
||||
if (mNestCount > 0) {
|
||||
for (int i = 0; i < mNestCount; i++) {
|
||||
if (!readBeginVCard(allowGarbage)) {
|
||||
return false;
|
||||
}
|
||||
allowGarbage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!readBeginVCard(allowGarbage)) {
|
||||
return false;
|
||||
}
|
||||
long start;
|
||||
if (mBuilder != null) {
|
||||
start = System.currentTimeMillis();
|
||||
mBuilder.startEntry();
|
||||
mTimeReadStartRecord += System.currentTimeMillis() - start;
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
parseItems();
|
||||
mTimeParseItems += System.currentTimeMillis() - start;
|
||||
readEndVCard(true, false);
|
||||
if (mBuilder != null) {
|
||||
start = System.currentTimeMillis();
|
||||
mBuilder.endEntry();
|
||||
mTimeReadEndRecord += System.currentTimeMillis() - start;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True when successful. False when reaching the end of line
|
||||
* @throws IOException
|
||||
* @throws VCardException
|
||||
*/
|
||||
protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
|
||||
String line;
|
||||
do {
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
return false;
|
||||
} else if (line.trim().length() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String[] strArray = line.split(":", 2);
|
||||
int length = strArray.length;
|
||||
|
||||
// Though vCard 2.1/3.0 specification does not allow lower cases,
|
||||
// vCard file emitted by some external vCard expoter have such invalid Strings.
|
||||
// So we allow it.
|
||||
// e.g. BEGIN:vCard
|
||||
if (length == 2 &&
|
||||
strArray[0].trim().equalsIgnoreCase("BEGIN") &&
|
||||
strArray[1].trim().equalsIgnoreCase("VCARD")) {
|
||||
return true;
|
||||
} else if (!allowGarbage) {
|
||||
if (mNestCount > 0) {
|
||||
mPreviousLine = line;
|
||||
return false;
|
||||
} else {
|
||||
throw new VCardException(
|
||||
"Expected String \"BEGIN:VCARD\" did not come "
|
||||
+ "(Instead, \"" + line + "\" came)");
|
||||
}
|
||||
}
|
||||
} while(allowGarbage);
|
||||
|
||||
throw new VCardException("Reached where must not be reached.");
|
||||
}
|
||||
|
||||
/**
|
||||
* The arguments useCache and allowGarbase are usually true and false accordingly when
|
||||
* this function is called outside this function itself.
|
||||
*
|
||||
* @param useCache When true, line is obtained from mPreviousline. Otherwise, getLine()
|
||||
* is used.
|
||||
* @param allowGarbage When true, ignore non "END:VCARD" line.
|
||||
* @throws IOException
|
||||
* @throws VCardException
|
||||
*/
|
||||
protected void readEndVCard(boolean useCache, boolean allowGarbage)
|
||||
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException {
|
||||
String line;
|
||||
do {
|
||||
if (useCache) {
|
||||
// Though vCard specification does not allow lower cases,
|
||||
// some data may have them, so we allow it.
|
||||
line = mPreviousLine;
|
||||
} else {
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException("Expected END:VCARD was not found.");
|
||||
} else if (line.trim().length() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String[] strArray = line.split(":", 2);
|
||||
if (strArray.length == 2 &&
|
||||
strArray[0].trim().equalsIgnoreCase("END") &&
|
||||
strArray[1].trim().equalsIgnoreCase("VCARD")) {
|
||||
return;
|
||||
} else if (!allowGarbage) {
|
||||
throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
|
||||
}
|
||||
useCache = false;
|
||||
} while (allowGarbage);
|
||||
}
|
||||
|
||||
/**
|
||||
* items = *CRLF item
|
||||
* / item
|
||||
*/
|
||||
protected void parseItems() throws IOException, VCardException {
|
||||
boolean ended = false;
|
||||
|
||||
if (mBuilder != null) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.startProperty();
|
||||
mTimeStartProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
ended = parseItem();
|
||||
if (mBuilder != null && !ended) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.endProperty();
|
||||
mTimeEndProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
while (!ended) {
|
||||
// follow VCARD ,it wont reach endProperty
|
||||
if (mBuilder != null) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.startProperty();
|
||||
mTimeStartProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
try {
|
||||
ended = parseItem();
|
||||
} catch (VCardInvalidCommentLineException e) {
|
||||
Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
|
||||
ended = false;
|
||||
}
|
||||
if (mBuilder != null && !ended) {
|
||||
long start = System.currentTimeMillis();
|
||||
mBuilder.endProperty();
|
||||
mTimeEndProperty += System.currentTimeMillis() - start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* item = [groups "."] name [params] ":" value CRLF
|
||||
* / [groups "."] "ADR" [params] ":" addressparts CRLF
|
||||
* / [groups "."] "ORG" [params] ":" orgparts CRLF
|
||||
* / [groups "."] "N" [params] ":" nameparts CRLF
|
||||
* / [groups "."] "AGENT" [params] ":" vcard CRLF
|
||||
*/
|
||||
protected boolean parseItem() throws IOException, VCardException {
|
||||
mEncoding = sDefaultEncoding;
|
||||
|
||||
final String line = getNonEmptyLine();
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
String[] propertyNameAndValue = separateLineAndHandleGroup(line);
|
||||
if (propertyNameAndValue == null) {
|
||||
return true;
|
||||
}
|
||||
if (propertyNameAndValue.length != 2) {
|
||||
throw new VCardInvalidLineException("Invalid line \"" + line + "\"");
|
||||
}
|
||||
String propertyName = propertyNameAndValue[0].toUpperCase();
|
||||
String propertyValue = propertyNameAndValue[1];
|
||||
|
||||
mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start;
|
||||
|
||||
if (propertyName.equals("ADR") || propertyName.equals("ORG") ||
|
||||
propertyName.equals("N")) {
|
||||
start = System.currentTimeMillis();
|
||||
handleMultiplePropertyValue(propertyName, propertyValue);
|
||||
mTimeParseAdrOrgN += System.currentTimeMillis() - start;
|
||||
return false;
|
||||
} else if (propertyName.equals("AGENT")) {
|
||||
handleAgent(propertyValue);
|
||||
return false;
|
||||
} else if (isValidPropertyName(propertyName)) {
|
||||
if (propertyName.equals("BEGIN")) {
|
||||
if (propertyValue.equals("VCARD")) {
|
||||
throw new VCardNestedException("This vCard has nested vCard data in it.");
|
||||
} else {
|
||||
throw new VCardException("Unknown BEGIN type: " + propertyValue);
|
||||
}
|
||||
} else if (propertyName.equals("VERSION") &&
|
||||
!propertyValue.equals(getVersionString())) {
|
||||
throw new VCardVersionException("Incompatible version: " +
|
||||
propertyValue + " != " + getVersionString());
|
||||
}
|
||||
start = System.currentTimeMillis();
|
||||
handlePropertyValue(propertyName, propertyValue);
|
||||
mTimeParsePropertyValues += System.currentTimeMillis() - start;
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new VCardException("Unknown property name: \"" + propertyName + "\"");
|
||||
mVCardParserImpl.parse(is, interepreter);
|
||||
}
|
||||
|
||||
static private final int STATE_GROUP_OR_PROPNAME = 0;
|
||||
static private final int STATE_PARAMS = 1;
|
||||
// vCard 3.0 specification allows double-quoted param-value, while vCard 2.1 does not.
|
||||
// This is just for safety.
|
||||
static private final int STATE_PARAMS_IN_DQUOTE = 2;
|
||||
|
||||
protected String[] separateLineAndHandleGroup(String line) throws VCardException {
|
||||
int state = STATE_GROUP_OR_PROPNAME;
|
||||
int nameIndex = 0;
|
||||
|
||||
final String[] propertyNameAndValue = new String[2];
|
||||
|
||||
final int length = line.length();
|
||||
if (length > 0 && line.charAt(0) == '#') {
|
||||
throw new VCardInvalidCommentLineException();
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = line.charAt(i);
|
||||
switch (state) {
|
||||
case STATE_GROUP_OR_PROPNAME: {
|
||||
if (ch == ':') {
|
||||
final String propertyName = line.substring(nameIndex, i);
|
||||
if (propertyName.equalsIgnoreCase("END")) {
|
||||
mPreviousLine = line;
|
||||
return null;
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyName(propertyName);
|
||||
}
|
||||
propertyNameAndValue[0] = propertyName;
|
||||
if (i < length - 1) {
|
||||
propertyNameAndValue[1] = line.substring(i + 1);
|
||||
} else {
|
||||
propertyNameAndValue[1] = "";
|
||||
}
|
||||
return propertyNameAndValue;
|
||||
} else if (ch == '.') {
|
||||
String groupName = line.substring(nameIndex, i);
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyGroup(groupName);
|
||||
}
|
||||
nameIndex = i + 1;
|
||||
} else if (ch == ';') {
|
||||
String propertyName = line.substring(nameIndex, i);
|
||||
if (propertyName.equalsIgnoreCase("END")) {
|
||||
mPreviousLine = line;
|
||||
return null;
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyName(propertyName);
|
||||
}
|
||||
propertyNameAndValue[0] = propertyName;
|
||||
nameIndex = i + 1;
|
||||
state = STATE_PARAMS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_PARAMS: {
|
||||
if (ch == '"') {
|
||||
state = STATE_PARAMS_IN_DQUOTE;
|
||||
} else if (ch == ';') {
|
||||
handleParams(line.substring(nameIndex, i));
|
||||
nameIndex = i + 1;
|
||||
} else if (ch == ':') {
|
||||
handleParams(line.substring(nameIndex, i));
|
||||
if (i < length - 1) {
|
||||
propertyNameAndValue[1] = line.substring(i + 1);
|
||||
} else {
|
||||
propertyNameAndValue[1] = "";
|
||||
}
|
||||
return propertyNameAndValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_PARAMS_IN_DQUOTE: {
|
||||
if (ch == '"') {
|
||||
state = STATE_PARAMS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new VCardInvalidLineException("Invalid line: \"" + line + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* params = ";" [ws] paramlist
|
||||
* paramlist = paramlist [ws] ";" [ws] param
|
||||
* / param
|
||||
* param = "TYPE" [ws] "=" [ws] ptypeval
|
||||
* / "VALUE" [ws] "=" [ws] pvalueval
|
||||
* / "ENCODING" [ws] "=" [ws] pencodingval
|
||||
* / "CHARSET" [ws] "=" [ws] charsetval
|
||||
* / "LANGUAGE" [ws] "=" [ws] langval
|
||||
* / "X-" word [ws] "=" [ws] word
|
||||
* / knowntype
|
||||
*/
|
||||
protected void handleParams(String params) throws VCardException {
|
||||
String[] strArray = params.split("=", 2);
|
||||
if (strArray.length == 2) {
|
||||
final String paramName = strArray[0].trim().toUpperCase();
|
||||
String paramValue = strArray[1].trim();
|
||||
if (paramName.equals("TYPE")) {
|
||||
handleType(paramValue);
|
||||
} else if (paramName.equals("VALUE")) {
|
||||
handleValue(paramValue);
|
||||
} else if (paramName.equals("ENCODING")) {
|
||||
handleEncoding(paramValue);
|
||||
} else if (paramName.equals("CHARSET")) {
|
||||
handleCharset(paramValue);
|
||||
} else if (paramName.equals("LANGUAGE")) {
|
||||
handleLanguage(paramValue);
|
||||
} else if (paramName.startsWith("X-")) {
|
||||
handleAnyParam(paramName, paramValue);
|
||||
} else {
|
||||
throw new VCardException("Unknown type \"" + paramName + "\"");
|
||||
}
|
||||
} else {
|
||||
handleParamWithoutName(strArray[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 parser may throw VCardException.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
protected void handleParamWithoutName(final String paramValue) throws VCardException {
|
||||
handleType(paramValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* ptypeval = knowntype / "X-" word
|
||||
*/
|
||||
protected void handleType(final String ptypeval) {
|
||||
String upperTypeValue = ptypeval;
|
||||
if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) &&
|
||||
!mUnknownTypeMap.contains(ptypeval)) {
|
||||
mUnknownTypeMap.add(ptypeval);
|
||||
Log.w(LOG_TAG, "TYPE unsupported by vCard 2.1: " + ptypeval);
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("TYPE");
|
||||
mBuilder.propertyParamValue(upperTypeValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
|
||||
*/
|
||||
protected void handleValue(final String pvalueval) {
|
||||
if (!sKnownValueSet.contains(pvalueval.toUpperCase()) &&
|
||||
pvalueval.startsWith("X-") &&
|
||||
!mUnknownValueMap.contains(pvalueval)) {
|
||||
mUnknownValueMap.add(pvalueval);
|
||||
Log.w(LOG_TAG, "VALUE unsupported by vCard 2.1: " + pvalueval);
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("VALUE");
|
||||
mBuilder.propertyParamValue(pvalueval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
|
||||
*/
|
||||
protected void handleEncoding(String pencodingval) throws VCardException {
|
||||
if (isValidEncoding(pencodingval) ||
|
||||
pencodingval.startsWith("X-")) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("ENCODING");
|
||||
mBuilder.propertyParamValue(pencodingval);
|
||||
}
|
||||
mEncoding = pencodingval;
|
||||
} else {
|
||||
throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
|
||||
* but today's vCard often contains other charset, so we allow them.
|
||||
*/
|
||||
protected void handleCharset(String charsetval) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("CHARSET");
|
||||
mBuilder.propertyParamValue(charsetval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See also Section 7.1 of RFC 1521
|
||||
*/
|
||||
protected void handleLanguage(String langval) throws VCardException {
|
||||
String[] strArray = langval.split("-");
|
||||
if (strArray.length != 2) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
String tmp = strArray[0];
|
||||
int length = tmp.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isLetter(tmp.charAt(i))) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
}
|
||||
tmp = strArray[1];
|
||||
length = tmp.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isLetter(tmp.charAt(i))) {
|
||||
throw new VCardException("Invalid Language: \"" + langval + "\"");
|
||||
}
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("LANGUAGE");
|
||||
mBuilder.propertyParamValue(langval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly for "X-" type. This accepts any kind of type without check.
|
||||
*/
|
||||
protected void handleAnyParam(String paramName, String paramValue) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType(paramName);
|
||||
mBuilder.propertyParamValue(paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePropertyValue(String propertyName, String propertyValue)
|
||||
throws IOException, VCardException {
|
||||
if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
|
||||
final long start = System.currentTimeMillis();
|
||||
final String result = getQuotedPrintable(propertyValue);
|
||||
if (mBuilder != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(result);
|
||||
mBuilder.propertyValues(v);
|
||||
}
|
||||
mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
|
||||
} else if (mEncoding.equalsIgnoreCase("BASE64") ||
|
||||
mEncoding.equalsIgnoreCase("B")) {
|
||||
final long start = System.currentTimeMillis();
|
||||
// It is very rare, but some BASE64 data may be so big that
|
||||
// OutOfMemoryError occurs. To ignore such cases, use try-catch.
|
||||
try {
|
||||
final String result = getBase64(propertyValue);
|
||||
if (mBuilder != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(result);
|
||||
mBuilder.propertyValues(v);
|
||||
}
|
||||
} catch (OutOfMemoryError error) {
|
||||
Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyValues(null);
|
||||
}
|
||||
}
|
||||
mTimeHandleBase64 += System.currentTimeMillis() - start;
|
||||
} else {
|
||||
if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT")
|
||||
|| mEncoding.equalsIgnoreCase("8BIT")
|
||||
|| mEncoding.toUpperCase().startsWith("X-"))) {
|
||||
Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\".");
|
||||
}
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
if (mBuilder != null) {
|
||||
ArrayList<String> v = new ArrayList<String>();
|
||||
v.add(maybeUnescapeText(propertyValue));
|
||||
mBuilder.propertyValues(v);
|
||||
}
|
||||
mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getQuotedPrintable(String firstString) throws IOException, VCardException {
|
||||
// Specifically, there may be some padding between = and CRLF.
|
||||
// See the following:
|
||||
//
|
||||
// qp-line := *(qp-segment transport-padding CRLF)
|
||||
// qp-part transport-padding
|
||||
// qp-segment := qp-section *(SPACE / TAB) "="
|
||||
// ; Maximum length of 76 characters
|
||||
//
|
||||
// e.g. (from RFC 2045)
|
||||
// Now's the time =
|
||||
// for all folk to come=
|
||||
// to the aid of their country.
|
||||
if (firstString.trim().endsWith("=")) {
|
||||
// remove "transport-padding"
|
||||
int pos = firstString.length() - 1;
|
||||
while(firstString.charAt(pos) != '=') {
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString.substring(0, pos + 1));
|
||||
builder.append("\r\n");
|
||||
String line;
|
||||
while (true) {
|
||||
line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException(
|
||||
"File ended during parsing quoted-printable String");
|
||||
}
|
||||
if (line.trim().endsWith("=")) {
|
||||
// remove "transport-padding"
|
||||
pos = line.length() - 1;
|
||||
while(line.charAt(pos) != '=') {
|
||||
}
|
||||
builder.append(line.substring(0, pos + 1));
|
||||
builder.append("\r\n");
|
||||
} else {
|
||||
builder.append(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
} else {
|
||||
return firstString;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getBase64(String firstString) throws IOException, VCardException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString);
|
||||
|
||||
while (true) {
|
||||
String line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException(
|
||||
"File ended during parsing BASE64 binary");
|
||||
}
|
||||
if (line.length() == 0) {
|
||||
break;
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly for "ADR", "ORG", and "N"
|
||||
* We do not care the number of strnosemi here.
|
||||
*
|
||||
* addressparts = 0*6(strnosemi ";") strnosemi
|
||||
* ; PO Box, Extended Addr, Street, Locality, Region,
|
||||
* Postal Code, Country Name
|
||||
* orgparts = *(strnosemi ";") strnosemi
|
||||
* ; First is Organization Name,
|
||||
* remainder are Organization Units.
|
||||
* nameparts = 0*4(strnosemi ";") strnosemi
|
||||
* ; Family, Given, Middle, Prefix, Suffix.
|
||||
* ; Example:Public;John;Q.;Reverend Dr.;III, Esq.
|
||||
* strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi
|
||||
* ; To include a semicolon in this string, it must be escaped
|
||||
* ; with a "\" character.
|
||||
*
|
||||
* We are not sure whether we should add "\" CRLF to each value.
|
||||
* For now, we exclude them.
|
||||
*/
|
||||
protected void handleMultiplePropertyValue(String propertyName, String propertyValue)
|
||||
throws IOException, VCardException {
|
||||
// vCard 2.1 does not allow QUOTED-PRINTABLE here,
|
||||
// but some softwares/devices emit such data.
|
||||
if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
|
||||
propertyValue = getQuotedPrintable(propertyValue);
|
||||
}
|
||||
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyValues(VCardUtils.constructListFromValue(
|
||||
propertyValue, (getVersion() == VCardConfig.FLAG_V30)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all.
|
||||
*
|
||||
* item = ...
|
||||
* / [groups "."] "AGENT"
|
||||
* [params] ":" vcard CRLF
|
||||
* vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
|
||||
* items *CRLF "END" [ws] ":" [ws] "VCARD"
|
||||
*/
|
||||
protected void handleAgent(final String propertyValue) throws VCardException {
|
||||
if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) {
|
||||
// Apparently invalid line seen in Windows Mobile 6.5. Ignore them.
|
||||
return;
|
||||
} else {
|
||||
throw new VCardAgentNotSupportedException("AGENT Property is not supported now.");
|
||||
}
|
||||
// TODO: Support AGENT property.
|
||||
}
|
||||
|
||||
/**
|
||||
* For vCard 3.0.
|
||||
*/
|
||||
protected String maybeUnescapeText(final String text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unescaped String if the character should be unescaped. Return null otherwise.
|
||||
* e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be.
|
||||
*/
|
||||
protected String maybeUnescapeCharacter(final char ch) {
|
||||
return unescapeCharacter(ch);
|
||||
}
|
||||
|
||||
public static String unescapeCharacter(final char ch) {
|
||||
// Original vCard 2.1 specification does not allow transformation
|
||||
// "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of
|
||||
// this class allowed them, so keep it as is.
|
||||
if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
|
||||
return String.valueOf(ch);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(final InputStream is, final VCardInterpreter builder)
|
||||
throws IOException, VCardException {
|
||||
return parse(is, VCardConfig.DEFAULT_CHARSET, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(InputStream is, String charset, VCardInterpreter builder)
|
||||
throws IOException, VCardException {
|
||||
if (charset == null) {
|
||||
charset = VCardConfig.DEFAULT_CHARSET;
|
||||
}
|
||||
final InputStreamReader tmpReader = new InputStreamReader(is, charset);
|
||||
if (VCardConfig.showPerformanceLog()) {
|
||||
mReader = new CustomBufferedReader(tmpReader);
|
||||
} else {
|
||||
mReader = new BufferedReader(tmpReader);
|
||||
}
|
||||
|
||||
mBuilder = builder;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
if (mBuilder != null) {
|
||||
mBuilder.start();
|
||||
}
|
||||
parseVCardFile();
|
||||
if (mBuilder != null) {
|
||||
mBuilder.end();
|
||||
}
|
||||
mTimeTotal += System.currentTimeMillis() - start;
|
||||
|
||||
if (VCardConfig.showPerformanceLog()) {
|
||||
showPerformanceInfo();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(InputStream is, String charset, VCardInterpreter builder, boolean canceled)
|
||||
throws IOException, VCardException {
|
||||
mCanceled = canceled;
|
||||
parse(is, charset, builder);
|
||||
}
|
||||
|
||||
private void showPerformanceInfo() {
|
||||
Log.d(LOG_TAG, "Total parsing time: " + mTimeTotal + " ms");
|
||||
if (mReader instanceof CustomBufferedReader) {
|
||||
Log.d(LOG_TAG, "Total readLine time: " +
|
||||
((CustomBufferedReader)mReader).getTotalmillisecond() + " ms");
|
||||
}
|
||||
Log.d(LOG_TAG, "Time for handling the beggining of the record: " +
|
||||
mTimeReadStartRecord + " ms");
|
||||
Log.d(LOG_TAG, "Time for handling the end of the record: " +
|
||||
mTimeReadEndRecord + " ms");
|
||||
Log.d(LOG_TAG, "Time for parsing line, and handling group: " +
|
||||
mTimeParseLineAndHandleGroup + " ms");
|
||||
Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms");
|
||||
Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms");
|
||||
Log.d(LOG_TAG, "Time for handling normal property values: " +
|
||||
mTimeHandleMiscPropertyValue + " ms");
|
||||
Log.d(LOG_TAG, "Time for handling Quoted-Printable: " +
|
||||
mTimeHandleQuotedPrintable + " ms");
|
||||
Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms");
|
||||
}
|
||||
|
||||
private boolean isLetter(char ch) {
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomBufferedReader extends BufferedReader {
|
||||
private long mTime;
|
||||
|
||||
public CustomBufferedReader(Reader in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine() throws IOException {
|
||||
long start = System.currentTimeMillis();
|
||||
String ret = super.readLine();
|
||||
long end = System.currentTimeMillis();
|
||||
mTime += end - start;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public long getTotalmillisecond() {
|
||||
return mTime;
|
||||
public void cancel() {
|
||||
mVCardParserImpl.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,343 +16,72 @@
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The class used to parse vCard 3.0.
|
||||
* Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426).
|
||||
* <p>
|
||||
* vCard parser for vCard 3.0. See RFC 2426 for more detail.
|
||||
* </p>
|
||||
* <p>
|
||||
* This parser allows vCard format which is not allowed in the RFC, since
|
||||
* we have seen several vCard 3.0 files which don't comply with it.
|
||||
* </p>
|
||||
* <p>
|
||||
* e.g. vCard 3.0 does not allow "CHARSET" attribute, but some actual files
|
||||
* have it and they uses non UTF-8 charsets. UTF-8 is recommended in RFC 2426,
|
||||
* but it is not a must. We silently allow "CHARSET".
|
||||
* </p>
|
||||
*/
|
||||
public class VCardParser_V30 extends VCardParser_V21 {
|
||||
private static final String LOG_TAG = "VCardParser_V30";
|
||||
|
||||
private static final HashSet<String> sAcceptablePropsWithParam = new HashSet<String>(
|
||||
Arrays.asList(
|
||||
public class VCardParser_V30 implements VCardParser {
|
||||
/* package */ static final Set<String> sKnownPropertyNameSet =
|
||||
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
|
||||
"BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
||||
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
||||
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
|
||||
"NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
|
||||
"SORT-STRING", "CATEGORIES", "PRODID")); // 3.0
|
||||
|
||||
// Although "7bit" and "BASE64" is not allowed in vCard 3.0, we allow it for safety.
|
||||
private static final HashSet<String> sAcceptableEncodingV30 = new HashSet<String>(
|
||||
Arrays.asList("7BIT", "8BIT", "BASE64", "B"));
|
||||
|
||||
// Although RFC 2426 specifies some property must not have parameters, we allow it,
|
||||
// since there may be some careers which violates the RFC...
|
||||
private static final HashSet<String> acceptablePropsWithoutParam = new HashSet<String>();
|
||||
|
||||
private String mPreviousLine;
|
||||
|
||||
private boolean mEmittedAgentWarning = false;
|
||||
"SORT-STRING", "CATEGORIES", "PRODID"))); // 3.0
|
||||
|
||||
/**
|
||||
* True when the caller wants the parser to be strict about the input.
|
||||
* Currently this is only for testing.
|
||||
* <p>
|
||||
* A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 3.0.
|
||||
* </p>
|
||||
* <p>
|
||||
* Though vCard 2.1 specification does not allow "7BIT" or "BASE64", we allow them for safety.
|
||||
* </p>
|
||||
* <p>
|
||||
* "QUOTED-PRINTABLE" is not allowed in vCard 3.0 and not in this parser either,
|
||||
* because the encoding ambiguates how the vCard file to be parsed.
|
||||
* </p>
|
||||
*/
|
||||
private final boolean mStrictParsing;
|
||||
/* package */ static final Set<String> sAcceptableEncoding =
|
||||
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
|
||||
VCardConstants.PARAM_ENCODING_7BIT,
|
||||
VCardConstants.PARAM_ENCODING_8BIT,
|
||||
VCardConstants.PARAM_ENCODING_BASE64,
|
||||
VCardConstants.PARAM_ENCODING_B)));
|
||||
|
||||
private final VCardParserImpl_V30 mVCardParserImpl;
|
||||
|
||||
public VCardParser_V30() {
|
||||
super();
|
||||
mStrictParsing = false;
|
||||
mVCardParserImpl = new VCardParserImpl_V30();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param strictParsing when true, this object throws VCardException when the vcard is not
|
||||
* valid from the view of vCard 3.0 specification (defined in RFC 2426). Note that this class
|
||||
* is not fully yet for being used with this flag and may not notice invalid line(s).
|
||||
*
|
||||
* @hide currently only for testing!
|
||||
*/
|
||||
public VCardParser_V30(boolean strictParsing) {
|
||||
super();
|
||||
mStrictParsing = strictParsing;
|
||||
public VCardParser_V30(int vcardType) {
|
||||
mVCardParserImpl = new VCardParserImpl_V30(vcardType);
|
||||
}
|
||||
|
||||
public VCardParser_V30(int parseMode) {
|
||||
super(parseMode);
|
||||
mStrictParsing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return VCardConfig.FLAG_V30;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVersionString() {
|
||||
return VCardConstants.VERSION_V30;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidPropertyName(String propertyName) {
|
||||
if (!(sAcceptablePropsWithParam.contains(propertyName) ||
|
||||
acceptablePropsWithoutParam.contains(propertyName) ||
|
||||
propertyName.startsWith("X-")) &&
|
||||
!mUnknownTypeMap.contains(propertyName)) {
|
||||
mUnknownTypeMap.add(propertyName);
|
||||
Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidEncoding(String encoding) {
|
||||
return sAcceptableEncodingV30.contains(encoding.toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLine() throws IOException {
|
||||
if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
} else {
|
||||
return mReader.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 requires that the line with space at the beginning of the line
|
||||
* must be combined with previous line.
|
||||
*/
|
||||
@Override
|
||||
protected String getNonEmptyLine() throws IOException, VCardException {
|
||||
String line;
|
||||
StringBuilder builder = null;
|
||||
while (true) {
|
||||
line = mReader.readLine();
|
||||
if (line == null) {
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
} else if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
}
|
||||
throw new VCardException("Reached end of buffer.");
|
||||
} else if (line.length() == 0) {
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
} else if (mPreviousLine != null) {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = null;
|
||||
return ret;
|
||||
}
|
||||
} else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
|
||||
if (builder != null) {
|
||||
// See Section 5.8.1 of RFC 2425 (MIME-DIR document).
|
||||
// Following is the excerpts from it.
|
||||
//
|
||||
// DESCRIPTION:This is a long description that exists on a long line.
|
||||
//
|
||||
// Can be represented as:
|
||||
//
|
||||
// DESCRIPTION:This is a long description
|
||||
// that exists on a long line.
|
||||
//
|
||||
// It could also be represented as:
|
||||
//
|
||||
// DESCRIPTION:This is a long descrip
|
||||
// tion that exists o
|
||||
// n a long line.
|
||||
builder.append(line.substring(1));
|
||||
} else if (mPreviousLine != null) {
|
||||
builder = new StringBuilder();
|
||||
builder.append(mPreviousLine);
|
||||
mPreviousLine = null;
|
||||
builder.append(line.substring(1));
|
||||
} else {
|
||||
throw new VCardException("Space exists at the beginning of the line");
|
||||
}
|
||||
} else {
|
||||
if (mPreviousLine == null) {
|
||||
mPreviousLine = line;
|
||||
if (builder != null) {
|
||||
return builder.toString();
|
||||
}
|
||||
} else {
|
||||
String ret = mPreviousLine;
|
||||
mPreviousLine = line;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF
|
||||
* 1 * (contentline)
|
||||
* ;A vCard object MUST include the VERSION, FN and N types.
|
||||
* [group "."] "END" ":" "VCARD" 1 * CRLF
|
||||
*/
|
||||
@Override
|
||||
protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
|
||||
// TODO: vCard 3.0 supports group.
|
||||
return super.readBeginVCard(allowGarbage);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readEndVCard(boolean useCache, boolean allowGarbage)
|
||||
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException {
|
||||
// TODO: vCard 3.0 supports group.
|
||||
super.readEndVCard(useCache, allowGarbage);
|
||||
mVCardParserImpl.parse(is, interepreter);
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
|
||||
*/
|
||||
@Override
|
||||
protected void handleParams(String params) throws VCardException {
|
||||
try {
|
||||
super.handleParams(params);
|
||||
} catch (VCardException e) {
|
||||
// maybe IANA type
|
||||
String[] strArray = params.split("=", 2);
|
||||
if (strArray.length == 2) {
|
||||
handleAnyParam(strArray[0], strArray[1]);
|
||||
} else {
|
||||
// Must not come here in the current implementation.
|
||||
throw new VCardException(
|
||||
"Unknown params value: " + params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAnyParam(String paramName, String paramValue) {
|
||||
super.handleAnyParam(paramName, paramValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleParamWithoutName(final String paramValue) throws VCardException {
|
||||
if (mStrictParsing) {
|
||||
throw new VCardException("Parameter without name is not acceptable in vCard 3.0");
|
||||
} else {
|
||||
super.handleParamWithoutName(paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 defines
|
||||
*
|
||||
* param = param-name "=" param-value *("," param-value)
|
||||
* param-name = iana-token / x-name
|
||||
* param-value = ptext / quoted-string
|
||||
* quoted-string = DQUOTE QSAFE-CHAR DQUOTE
|
||||
*/
|
||||
@Override
|
||||
protected void handleType(String ptypevalues) {
|
||||
String[] ptypeArray = ptypevalues.split(",");
|
||||
mBuilder.propertyParamType("TYPE");
|
||||
for (String value : ptypeArray) {
|
||||
int length = value.length();
|
||||
if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
|
||||
mBuilder.propertyParamValue(value.substring(1, value.length() - 1));
|
||||
} else {
|
||||
mBuilder.propertyParamValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAgent(String propertyValue) {
|
||||
// The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
|
||||
//
|
||||
// e.g.
|
||||
// AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
|
||||
// TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
|
||||
// ET:jfriday@host.com\nEND:VCARD\n
|
||||
//
|
||||
// TODO: fix this.
|
||||
//
|
||||
// issue:
|
||||
// vCard 3.0 also allows this as an example.
|
||||
//
|
||||
// AGENT;VALUE=uri:
|
||||
// CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
|
||||
//
|
||||
// This is not vCard. Should we support this?
|
||||
//
|
||||
// Just ignore the line for now, since we cannot know how to handle it...
|
||||
if (!mEmittedAgentWarning) {
|
||||
Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
|
||||
mEmittedAgentWarning = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vCard 3.0 does not require two CRLF at the last of BASE64 data.
|
||||
* It only requires that data should be MIME-encoded.
|
||||
*/
|
||||
@Override
|
||||
protected String getBase64(String firstString) throws IOException, VCardException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(firstString);
|
||||
|
||||
while (true) {
|
||||
String line = getLine();
|
||||
if (line == null) {
|
||||
throw new VCardException(
|
||||
"File ended during parsing BASE64 binary");
|
||||
}
|
||||
if (line.length() == 0) {
|
||||
break;
|
||||
} else if (!line.startsWith(" ") && !line.startsWith("\t")) {
|
||||
mPreviousLine = line;
|
||||
break;
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
|
||||
* ; \\ encodes \, \n or \N encodes newline
|
||||
* ; \; encodes ;, \, encodes ,
|
||||
*
|
||||
* Note: Apple escapes ':' into '\:' while does not escape '\'
|
||||
*/
|
||||
@Override
|
||||
protected String maybeUnescapeText(String text) {
|
||||
return unescapeText(text);
|
||||
}
|
||||
|
||||
public static String unescapeText(String text) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = text.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch == '\\' && i < length - 1) {
|
||||
char next_ch = text.charAt(++i);
|
||||
if (next_ch == 'n' || next_ch == 'N') {
|
||||
builder.append("\n");
|
||||
} else {
|
||||
builder.append(next_ch);
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String maybeUnescapeCharacter(char ch) {
|
||||
return unescapeCharacter(ch);
|
||||
}
|
||||
|
||||
public static String unescapeCharacter(char ch) {
|
||||
if (ch == 'n' || ch == 'N') {
|
||||
return "\n";
|
||||
} else {
|
||||
return String.valueOf(ch);
|
||||
}
|
||||
public void cancel() {
|
||||
mVCardParserImpl.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
78
core/java/android/pim/vcard/VCardParser_V40.java
Normal file
78
core/java/android/pim/vcard/VCardParser_V40.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* vCard parser for vCard 4.0.
|
||||
* </p>
|
||||
* <p>
|
||||
* Currently this parser is based on vCard 4.0 specification rev 11.
|
||||
* </p>
|
||||
*/
|
||||
public class VCardParser_V40 implements VCardParser {
|
||||
/* package */ static final Set<String> sKnownPropertyNameSet =
|
||||
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
|
||||
"BEGIN", "END", "SOURCE", "NAME", "KIND", "XML",
|
||||
"FN", "N", "NICKNAME", "PHOTO", "BDAY", "DDAY",
|
||||
"BIRTH", "DEATH", "ANNIVERSARY", "SEX", "ADR",
|
||||
"LABEL", "TEL", "EMAIL", "IMPP", "LANG", "TZ",
|
||||
"GEO", "TITLE", "ROLE", "LOGO", "ORG", "MEMBER",
|
||||
"RELATED", "CATEGORIES", "NOTE", "PRODID",
|
||||
"REV", "SOUND", "UID", "CLIENTPIDMAP",
|
||||
"URL", "VERSION", "CLASS", "KEY", "FBURL", "CALENDRURI",
|
||||
"CALURI")));
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A unmodifiable Set storing the values for the type "ENCODING", available in vCard 4.0.
|
||||
* </p>
|
||||
*/
|
||||
/* package */ static final Set<String> sAcceptableEncoding =
|
||||
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
|
||||
VCardConstants.PARAM_ENCODING_8BIT,
|
||||
VCardConstants.PARAM_ENCODING_B)));
|
||||
|
||||
private final VCardParserImpl_V30 mVCardParserImpl;
|
||||
|
||||
public VCardParser_V40() {
|
||||
mVCardParserImpl = new VCardParserImpl_V40();
|
||||
}
|
||||
|
||||
public VCardParser_V40(int vcardType) {
|
||||
mVCardParserImpl = new VCardParserImpl_V40(vcardType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException {
|
||||
mVCardParserImpl.parse(is, interepreter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
mVCardParserImpl.cancel();
|
||||
}
|
||||
}
|
||||
@@ -15,17 +15,33 @@
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Class which tries to detects the source of the vCard from its properties.
|
||||
* Currently this implementation is very premature.
|
||||
* @hide
|
||||
* <p>
|
||||
* The class which tries to detects the source of a vCard file from its contents.
|
||||
* </p>
|
||||
* <p>
|
||||
* The specification of vCard (including both 2.1 and 3.0) is not so strict as to
|
||||
* guess its format just by reading beginning few lines (usually we can, but in
|
||||
* some most pessimistic case, we cannot until at almost the end of the file).
|
||||
* Also we cannot store all vCard entries in memory, while there's no specification
|
||||
* how big the vCard entry would become after the parse.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class is usually used for the "first scan", in which we can understand which vCard
|
||||
* version is used (and how many entries exist in a file).
|
||||
* </p>
|
||||
*/
|
||||
public class VCardSourceDetector implements VCardInterpreter {
|
||||
private static final String LOG_TAG = "VCardSourceDetector";
|
||||
|
||||
private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
|
||||
"X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
|
||||
"X-ABADR", "X-ABUID"));
|
||||
@@ -42,10 +58,30 @@ public class VCardSourceDetector implements VCardInterpreter {
|
||||
"X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
|
||||
"X-SD-DESCRIPTION"));
|
||||
private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
|
||||
|
||||
private int mType = VCardConfig.PARSE_TYPE_UNKNOWN;
|
||||
|
||||
/**
|
||||
* Represents that no estimation is available. Users of this class is able to this
|
||||
* constant when you don't want to let a vCard parser rely on estimation for parse type.
|
||||
*/
|
||||
public static final int PARSE_TYPE_UNKNOWN = 0;
|
||||
|
||||
// For Apple's software, which does not mean this type is effective for all its products.
|
||||
// We confirmed they usually use UTF-8, but not sure about vCard type.
|
||||
private static final int PARSE_TYPE_APPLE = 1;
|
||||
// For Japanese mobile phones, which are usually using Shift_JIS as a charset.
|
||||
private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2;
|
||||
// For some of mobile phones released from DoCoMo, which use nested vCard.
|
||||
private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3;
|
||||
// For Japanese Windows Mobel phones. It's version is supposed to be 6.5.
|
||||
private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4;
|
||||
|
||||
private int mParseType = 0; // Not sure.
|
||||
|
||||
private boolean mNeedToParseVersion = false;
|
||||
private int mVersion = -1; // -1 == unknown
|
||||
|
||||
// Some mobile phones (like FOMA) tells us the charset of the data.
|
||||
private boolean mNeedParseSpecifiedCharset;
|
||||
private boolean mNeedToParseCharset;
|
||||
private String mSpecifiedCharset;
|
||||
|
||||
public void start() {
|
||||
@@ -58,7 +94,8 @@ public class VCardSourceDetector implements VCardInterpreter {
|
||||
}
|
||||
|
||||
public void startProperty() {
|
||||
mNeedParseSpecifiedCharset = false;
|
||||
mNeedToParseCharset = false;
|
||||
mNeedToParseVersion = false;
|
||||
}
|
||||
|
||||
public void endProperty() {
|
||||
@@ -71,22 +108,26 @@ public class VCardSourceDetector implements VCardInterpreter {
|
||||
}
|
||||
|
||||
public void propertyName(String name) {
|
||||
if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
|
||||
mType = VCardConfig.PARSE_TYPE_FOMA;
|
||||
mNeedParseSpecifiedCharset = true;
|
||||
if (name.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION)) {
|
||||
mNeedToParseVersion = true;
|
||||
return;
|
||||
} else if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
|
||||
mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
|
||||
// Probably Shift_JIS is used, but we should double confirm.
|
||||
mNeedToParseCharset = true;
|
||||
return;
|
||||
}
|
||||
if (mType != VCardConfig.PARSE_TYPE_UNKNOWN) {
|
||||
if (mParseType != PARSE_TYPE_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
|
||||
mType = VCardConfig.PARSE_TYPE_WINDOWS_MOBILE_JP;
|
||||
mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP;
|
||||
} else if (FOMA_SIGNS.contains(name)) {
|
||||
mType = VCardConfig.PARSE_TYPE_FOMA;
|
||||
mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
|
||||
} else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
|
||||
mType = VCardConfig.PARSE_TYPE_MOBILE_PHONE_JP;
|
||||
mParseType = PARSE_TYPE_MOBILE_PHONE_JP;
|
||||
} else if (APPLE_SIGNS.contains(name)) {
|
||||
mType = VCardConfig.PARSE_TYPE_APPLE;
|
||||
mParseType = PARSE_TYPE_APPLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,30 +138,65 @@ public class VCardSourceDetector implements VCardInterpreter {
|
||||
}
|
||||
|
||||
public void propertyValues(List<String> values) {
|
||||
if (mNeedParseSpecifiedCharset && values.size() > 0) {
|
||||
if (mNeedToParseVersion && values.size() > 0) {
|
||||
final String versionString = values.get(0);
|
||||
if (versionString.equals(VCardConstants.VERSION_V21)) {
|
||||
mVersion = VCardConfig.VERSION_21;
|
||||
} else if (versionString.equals(VCardConstants.VERSION_V30)) {
|
||||
mVersion = VCardConfig.VERSION_30;
|
||||
} else if (versionString.equals(VCardConstants.VERSION_V40)) {
|
||||
mVersion = VCardConfig.VERSION_40;
|
||||
} else {
|
||||
Log.w(LOG_TAG, "Invalid version string: " + versionString);
|
||||
}
|
||||
} else if (mNeedToParseCharset && values.size() > 0) {
|
||||
mSpecifiedCharset = values.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ int getEstimatedType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return charset String guessed from the source's properties.
|
||||
* @return The available type can be used with vCard parser. You probably need to
|
||||
* use {{@link #getEstimatedCharset()} to understand the charset to be used.
|
||||
*/
|
||||
public int getEstimatedType() {
|
||||
switch (mParseType) {
|
||||
case PARSE_TYPE_DOCOMO_TORELATE_NEST:
|
||||
return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST;
|
||||
case PARSE_TYPE_MOBILE_PHONE_JP:
|
||||
return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE;
|
||||
case PARSE_TYPE_APPLE:
|
||||
case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
|
||||
default: {
|
||||
if (mVersion == VCardConfig.VERSION_21) {
|
||||
return VCardConfig.VCARD_TYPE_V21_GENERIC;
|
||||
} else if (mVersion == VCardConfig.VERSION_30) {
|
||||
return VCardConfig.VCARD_TYPE_V30_GENERIC;
|
||||
} else if (mVersion == VCardConfig.VERSION_40) {
|
||||
return VCardConfig.VCARD_TYPE_V40_GENERIC;
|
||||
} else {
|
||||
return VCardConfig.VCARD_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns charset String guessed from the source's properties.
|
||||
* This method must be called after parsing target file(s).
|
||||
* </p>
|
||||
* @return Charset String. Null is returned if guessing the source fails.
|
||||
*/
|
||||
public String getEstimatedCharset() {
|
||||
if (mSpecifiedCharset != null) {
|
||||
if (TextUtils.isEmpty(mSpecifiedCharset)) {
|
||||
return mSpecifiedCharset;
|
||||
}
|
||||
switch (mType) {
|
||||
case VCardConfig.PARSE_TYPE_WINDOWS_MOBILE_JP:
|
||||
case VCardConfig.PARSE_TYPE_FOMA:
|
||||
case VCardConfig.PARSE_TYPE_MOBILE_PHONE_JP:
|
||||
switch (mParseType) {
|
||||
case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
|
||||
case PARSE_TYPE_DOCOMO_TORELATE_NEST:
|
||||
case PARSE_TYPE_MOBILE_PHONE_JP:
|
||||
return "SHIFT_JIS";
|
||||
case VCardConfig.PARSE_TYPE_APPLE:
|
||||
case PARSE_TYPE_APPLE:
|
||||
return "UTF-8";
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -16,13 +16,21 @@
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.net.QuotedPrintableCodec;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -36,6 +44,8 @@ import java.util.Set;
|
||||
* Utilities for VCard handling codes.
|
||||
*/
|
||||
public class VCardUtils {
|
||||
private static final String LOG_TAG = "VCardUtils";
|
||||
|
||||
// Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
|
||||
// converted to two parameter Strings. These only contain some minor fields valid in both
|
||||
// vCard and current (as of 2009-08-07) Contacts structure.
|
||||
@@ -185,8 +195,7 @@ public class VCardUtils {
|
||||
// For backward compatibility.
|
||||
// Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
|
||||
// To support mobile type at that time, this custom label had been used.
|
||||
return (android.provider.Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME.equals(label)
|
||||
|| sMobilePhoneLabelSet.contains(label));
|
||||
return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label));
|
||||
}
|
||||
|
||||
public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) {
|
||||
@@ -240,10 +249,13 @@ public class VCardUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Inserts postal data into the builder object.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the data structure of ContactsContract is different from that defined in vCard.
|
||||
* So some conversion may be performed in this method.
|
||||
* </p>
|
||||
*/
|
||||
public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
|
||||
final ContentProviderOperation.Builder builder,
|
||||
@@ -319,18 +331,33 @@ public class VCardUtils {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the given value into pieces using the delimiter ';' inside it.
|
||||
*
|
||||
* Escaped characters in those values are automatically unescaped into original form.
|
||||
*/
|
||||
public static List<String> constructListFromValue(final String value,
|
||||
final boolean isV30) {
|
||||
final int vcardType) {
|
||||
final List<String> list = new ArrayList<String>();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = value.length();
|
||||
final int length = value.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = value.charAt(i);
|
||||
if (ch == '\\' && i < length - 1) {
|
||||
char nextCh = value.charAt(i + 1);
|
||||
final String unescapedString =
|
||||
(isV30 ? VCardParser_V30.unescapeCharacter(nextCh) :
|
||||
VCardParser_V21.unescapeCharacter(nextCh));
|
||||
final String unescapedString;
|
||||
if (VCardConfig.isVersion40(vcardType)) {
|
||||
unescapedString = VCardParserImpl_V40.unescapeCharacter(nextCh);
|
||||
} else if (VCardConfig.isVersion30(vcardType)) {
|
||||
unescapedString = VCardParserImpl_V30.unescapeCharacter(nextCh);
|
||||
} else {
|
||||
if (!VCardConfig.isVersion21(vcardType)) {
|
||||
// Unknown vCard type
|
||||
Log.w(LOG_TAG, "Unknown vCard type");
|
||||
}
|
||||
unescapedString = VCardParserImpl_V21.unescapeCharacter(nextCh);
|
||||
}
|
||||
|
||||
if (unescapedString != null) {
|
||||
builder.append(unescapedString);
|
||||
i++;
|
||||
@@ -371,9 +398,13 @@ public class VCardUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This is useful when checking the string should be encoded into quoted-printable
|
||||
* or not, which is required by vCard 2.1.
|
||||
* </p>
|
||||
* <p>
|
||||
* See the definition of "7bit" in vCard 2.1 spec for more information.
|
||||
* </p>
|
||||
*/
|
||||
public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) {
|
||||
if (values == null) {
|
||||
@@ -407,13 +438,16 @@ public class VCardUtils {
|
||||
new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This is useful since vCard 3.0 often requires the ("X-") properties and groups
|
||||
* should contain only alphabets, digits, and hyphen.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* 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 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.
|
||||
* to the device which is able to parse the malformed input.
|
||||
* </p>
|
||||
*/
|
||||
public static boolean containsOnlyAlphaDigitHyphen(final String...values) {
|
||||
if (values == null) {
|
||||
@@ -451,14 +485,39 @@ public class VCardUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean containsOnlyWhiteSpaces(final String...values) {
|
||||
if (values == null) {
|
||||
return true;
|
||||
}
|
||||
return containsOnlyWhiteSpaces(Arrays.asList(values));
|
||||
}
|
||||
|
||||
public static boolean containsOnlyWhiteSpaces(final Collection<String> values) {
|
||||
if (values == null) {
|
||||
return true;
|
||||
}
|
||||
for (final String str : values) {
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
continue;
|
||||
}
|
||||
final int length = str.length();
|
||||
for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
|
||||
if (!Character.isWhitespace(str.codePointAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* <p>
|
||||
* Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
|
||||
* </P>
|
||||
* <P>
|
||||
* vCard 2.1 specifies:<BR />
|
||||
* </p>
|
||||
* <p>
|
||||
* vCard 2.1 specifies:<br />
|
||||
* word = <any printable 7bit us-ascii except []=:., >
|
||||
* </P>
|
||||
* </p>
|
||||
*/
|
||||
public static boolean isV21Word(final String value) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
@@ -477,6 +536,14 @@ public class VCardUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final int[] sEscapeIndicatorsV30 = new int[]{
|
||||
':', ';', ',', ' '
|
||||
};
|
||||
|
||||
private static final int[] sEscapeIndicatorsV40 = new int[]{
|
||||
';', ':'
|
||||
};
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* Returns String available as parameter value in vCard 3.0.
|
||||
@@ -487,10 +554,18 @@ public class VCardUtils {
|
||||
* This method checks whether the given String can be used without quotes.
|
||||
* </P>
|
||||
* <P>
|
||||
* Note: We remove DQUOTE silently for now.
|
||||
* Note: We remove DQUOTE inside the given value silently for now.
|
||||
* </P>
|
||||
*/
|
||||
public static String toStringAvailableAsV30ParameValue(String value) {
|
||||
public static String toStringAsV30ParamValue(String value) {
|
||||
return toStringAsParamValue(value, sEscapeIndicatorsV30);
|
||||
}
|
||||
|
||||
public static String toStringAsV40ParamValue(String value) {
|
||||
return toStringAsParamValue(value, sEscapeIndicatorsV40);
|
||||
}
|
||||
|
||||
private static String toStringAsParamValue(String value, final int[] escapeIndicators) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
value = "";
|
||||
}
|
||||
@@ -506,12 +581,19 @@ public class VCardUtils {
|
||||
continue;
|
||||
}
|
||||
builder.appendCodePoint(codePoint);
|
||||
if (codePoint == ':' || codePoint == ',' || codePoint == ' ') {
|
||||
needQuote = true;
|
||||
for (int indicator : escapeIndicators) {
|
||||
if (codePoint == indicator) {
|
||||
needQuote = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String result = builder.toString();
|
||||
return ((needQuote || result.isEmpty()) ? ('"' + result + '"') : result);
|
||||
return ((result.isEmpty() || VCardUtils.containsOnlyWhiteSpaces(result))
|
||||
? ""
|
||||
: (needQuote ? ('"' + result + '"')
|
||||
: result));
|
||||
}
|
||||
|
||||
public static String toHalfWidthString(final String orgString) {
|
||||
@@ -577,6 +659,138 @@ public class VCardUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
//// The methods bellow may be used by unit test.
|
||||
|
||||
/**
|
||||
* Unquotes given Quoted-Printable value. value must not be null.
|
||||
*/
|
||||
public static String parseQuotedPrintable(
|
||||
final String value, boolean strictLineBreaking,
|
||||
String sourceCharset, String targetCharset) {
|
||||
// "= " -> " ", "=\t" -> "\t".
|
||||
// Previous code had done this replacement. Keep on the safe side.
|
||||
final String quotedPrintable;
|
||||
{
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final int length = value.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = value.charAt(i);
|
||||
if (ch == '=' && i < length - 1) {
|
||||
char nextCh = value.charAt(i + 1);
|
||||
if (nextCh == ' ' || nextCh == '\t') {
|
||||
builder.append(nextCh);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
builder.append(ch);
|
||||
}
|
||||
quotedPrintable = builder.toString();
|
||||
}
|
||||
|
||||
String[] lines;
|
||||
if (strictLineBreaking) {
|
||||
lines = quotedPrintable.split("\r\n");
|
||||
} else {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
final int length = quotedPrintable.length();
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = quotedPrintable.charAt(i);
|
||||
if (ch == '\n') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
} else if (ch == '\r') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
if (i < length - 1) {
|
||||
char nextCh = quotedPrintable.charAt(i + 1);
|
||||
if (nextCh == '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
final String lastLine = builder.toString();
|
||||
if (lastLine.length() > 0) {
|
||||
list.add(lastLine);
|
||||
}
|
||||
lines = list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
if (line.endsWith("=")) {
|
||||
line = line.substring(0, line.length() - 1);
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
final String rawString = builder.toString();
|
||||
if (TextUtils.isEmpty(rawString)) {
|
||||
Log.w(LOG_TAG, "Given raw string is empty.");
|
||||
}
|
||||
|
||||
byte[] rawBytes = null;
|
||||
try {
|
||||
rawBytes = rawString.getBytes(sourceCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.w(LOG_TAG, "Failed to decode: " + sourceCharset);
|
||||
rawBytes = rawString.getBytes();
|
||||
}
|
||||
|
||||
byte[] decodedBytes = null;
|
||||
try {
|
||||
decodedBytes = QuotedPrintableCodec.decodeQuotedPrintable(rawBytes);
|
||||
} catch (DecoderException e) {
|
||||
Log.e(LOG_TAG, "DecoderException is thrown.");
|
||||
decodedBytes = rawBytes;
|
||||
}
|
||||
|
||||
try {
|
||||
return new String(decodedBytes, targetCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return new String(decodedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public static final VCardParser getAppropriateParser(int vcardType)
|
||||
throws VCardException {
|
||||
if (VCardConfig.isVersion21(vcardType)) {
|
||||
return new VCardParser_V21();
|
||||
} else if (VCardConfig.isVersion30(vcardType)) {
|
||||
return new VCardParser_V30();
|
||||
} else if (VCardConfig.isVersion40(vcardType)) {
|
||||
return new VCardParser_V40();
|
||||
} else {
|
||||
throw new VCardException("Version is not specified");
|
||||
}
|
||||
}
|
||||
|
||||
public static final String convertStringCharset(
|
||||
String originalString, String sourceCharset, String targetCharset) {
|
||||
if (sourceCharset.equalsIgnoreCase(targetCharset)) {
|
||||
return originalString;
|
||||
}
|
||||
final Charset charset = Charset.forName(sourceCharset);
|
||||
final ByteBuffer byteBuffer = charset.encode(originalString);
|
||||
// byteBuffer.array() "may" return byte array which is larger than
|
||||
// byteBuffer.remaining(). Here, we keep on the safe side.
|
||||
final byte[] bytes = new byte[byteBuffer.remaining()];
|
||||
byteBuffer.get(bytes);
|
||||
try {
|
||||
return new String(bytes, targetCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: utilities for vCard 4.0: datetime, timestamp, integer, float, and boolean
|
||||
|
||||
private VCardUtils() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.pim.vcard.exception;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.pim.vcard.exception;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.VCardEntry;
|
||||
import android.pim.vcard.VCardEntryConstructor;
|
||||
import android.pim.vcard.VCardEntryHandler;
|
||||
import android.pim.vcard.VCardParser;
|
||||
import android.pim.vcard.VCardParser_V21;
|
||||
import android.pim.vcard.VCardParser_V30;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/* package */ class ContentValuesVerifier implements VCardEntryHandler {
|
||||
private AndroidTestCase mTestCase;
|
||||
private List<ContentValuesVerifierElem> mContentValuesVerifierElemList =
|
||||
new ArrayList<ContentValuesVerifierElem>();
|
||||
private int mIndex;
|
||||
|
||||
public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) {
|
||||
mTestCase = androidTestCase;
|
||||
ContentValuesVerifierElem importVerifier = new ContentValuesVerifierElem(androidTestCase);
|
||||
mContentValuesVerifierElemList.add(importVerifier);
|
||||
return importVerifier;
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType) throws IOException, VCardException {
|
||||
verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
verify(mTestCase.getContext().getResources().openRawResource(resId),
|
||||
vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
vCardParser = new VCardParser_V30(true); // use StrictParsing
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
verify(is, vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
VCardEntryConstructor builder =
|
||||
new VCardEntryConstructor(null, null, false, vCardType, null);
|
||||
builder.addEntryHandler(this);
|
||||
try {
|
||||
vCardParser.parse(is, builder);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
|
||||
elem.onParsingStart();
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryCreated(VCardEntry entry) {
|
||||
mTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size());
|
||||
mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry);
|
||||
mIndex++;
|
||||
}
|
||||
|
||||
public void onEnd() {
|
||||
for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
|
||||
elem.onParsingEnd();
|
||||
elem.verifyResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,10 @@
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.test_utils.ContactEntry;
|
||||
import android.pim.vcard.test_utils.PropertyNodesVerifierElem;
|
||||
import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
|
||||
import android.pim.vcard.test_utils.VCardTestsBase;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
||||
@@ -31,8 +34,6 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||
|
||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,6 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
private void testStructuredNameBasic(int vcardType) {
|
||||
final boolean isV30 = VCardConfig.isV30(vcardType);
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
||||
@@ -64,28 +64,15 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
|
||||
.put(StructuredName.DISPLAY_NAME, "DISPLAY NAME");
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||
.addExpectedNodeWithOrder("FN",
|
||||
"AppropriatePrefix AppropriateGivenName "
|
||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
|
||||
if (isV30) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle "
|
||||
+ "AppropriatePhoneticFamily");
|
||||
}
|
||||
.addExpectedNodeWithOrder("FN", "DISPLAY NAME");
|
||||
}
|
||||
|
||||
public void testStructuredNameBasicV21() {
|
||||
@@ -96,6 +83,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testStructuredNameBasic(V30);
|
||||
}
|
||||
|
||||
public void testStructuredNameBasicV40() {
|
||||
testStructuredNameBasic(V40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that only "primary" StructuredName is emitted, so that our vCard file
|
||||
* will not confuse the external importer, assuming there may be some importer
|
||||
@@ -103,18 +94,15 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
* Note that more than one "N", "FN", etc. properties are acceptable in vCard spec.
|
||||
*/
|
||||
private void testStructuredNameUsePrimaryCommon(int vcardType) {
|
||||
final boolean isV30 = (vcardType == V30);
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
final ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix1")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
|
||||
.put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplayName1");
|
||||
|
||||
// With "IS_PRIMARY=1". This is what we should use.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
@@ -123,41 +111,30 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
|
||||
.put(StructuredName.DISPLAY_NAME, "AppropriateDisplayName")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
// With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
|
||||
// vCard 2.1 does not specify anything about the number of N properties. We choose not
|
||||
// emitting this property.
|
||||
// vCard 3.0 does (There must be one N property)
|
||||
// vCard 4.0 (rev13) does (cardinality (0, 1)).
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix2")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2")
|
||||
.put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplayName2")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||
.addExpectedNodeWithOrder("FN",
|
||||
"AppropriatePrefix AppropriateGivenName "
|
||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
|
||||
if (isV30) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle "
|
||||
+ "AppropriatePhoneticFamily");
|
||||
}
|
||||
.addExpectedNodeWithOrder("FN", "AppropriateDisplayName");
|
||||
}
|
||||
|
||||
public void testStructuredNameUsePrimaryV21() {
|
||||
@@ -168,14 +145,152 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testStructuredNameUsePrimaryCommon(V30);
|
||||
}
|
||||
|
||||
public void testStructuredNameUsePrimaryV40() {
|
||||
testStructuredNameUsePrimaryCommon(V40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that only "super primary" StructuredName is emitted.
|
||||
* See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
|
||||
*/
|
||||
private void testStructuredNameUseSuperPrimaryCommon(int vcardType) {
|
||||
final boolean isV30 = (vcardType == V30);
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
final ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix1")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
|
||||
.put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplay1");
|
||||
|
||||
// With "IS_PRIMARY=1", but we should ignore this time.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix2")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
|
||||
.put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplay2")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
// With "IS_SUPER_PRIMARY=1". This is what we should use.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
||||
.put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
|
||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||
.put(StructuredName.DISPLAY_NAME, "AppropriateDisplayName")
|
||||
.put(StructuredName.IS_SUPER_PRIMARY, 1);
|
||||
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3")
|
||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3")
|
||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix3")
|
||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix3")
|
||||
.put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplay3")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem();
|
||||
elem.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"));
|
||||
|
||||
elem.addExpectedNodeWithOrder("FN", "AppropriateDisplayName");
|
||||
}
|
||||
|
||||
public void testStructuredNameUseSuperPrimaryV21() {
|
||||
testStructuredNameUseSuperPrimaryCommon(V21);
|
||||
}
|
||||
|
||||
public void testStructuredNameUseSuperPrimaryV30() {
|
||||
testStructuredNameUseSuperPrimaryCommon(V30);
|
||||
}
|
||||
|
||||
public void testStructuredNameUseSuperPrimaryV40() {
|
||||
testStructuredNameUseSuperPrimaryCommon(V40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests phonetic names field are handled correctly.
|
||||
*
|
||||
* vCard 2.1 does not have any field corresponding to them.
|
||||
* vCard 3.0 has SORT-STRING property, which does not support multiple values inside it.
|
||||
* vCard 4.0 (rev13) has SORT-AS parameter, which has three values (family, given, middle)
|
||||
* inside it.
|
||||
*/
|
||||
private void testStructuredNamePhoneticNameCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
final ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
||||
.put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
|
||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||
.put(StructuredName.DISPLAY_NAME, "AppropriateDisplayName")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
|
||||
|
||||
final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem();
|
||||
if (VCardConfig.isVersion40(vcardType)) {
|
||||
final ContentValues contentValues = new ContentValues();
|
||||
contentValues.put("SORT-AS",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName");
|
||||
// vCard 4.0 (rev13) now uses SORT-AS parameter, which is not compatible with
|
||||
// either 2.1 nor 3.0.
|
||||
elem.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"),
|
||||
contentValues);
|
||||
} else {
|
||||
elem.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"));
|
||||
if (VCardConfig.isVersion30(vcardType)) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle"
|
||||
+ " AppropriatePhoneticFamily");
|
||||
}
|
||||
}
|
||||
|
||||
elem.addExpectedNodeWithOrder("FN", "AppropriateDisplayName")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
}
|
||||
|
||||
public void testStructuredNamePhoneticNameV21() {
|
||||
testStructuredNamePhoneticNameCommon(V21);
|
||||
}
|
||||
|
||||
public void testStructuredNamePhoneticNameV30() {
|
||||
testStructuredNamePhoneticNameCommon(V30);
|
||||
}
|
||||
|
||||
public void testStructuredNamePhoneticNameV40() {
|
||||
testStructuredNamePhoneticNameCommon(V40);
|
||||
}
|
||||
|
||||
// TODO: need to add test cases confirming escaping, empty values, etc.
|
||||
|
||||
/**
|
||||
* Confirms all the other sides of the handling is correctly interpreted at one time.
|
||||
*
|
||||
* A kind of regression test for StructuredName handling.
|
||||
*/
|
||||
private void testStructuredNameComplicatedCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
final ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
||||
@@ -221,32 +336,50 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3")
|
||||
.put(StructuredName.IS_PRIMARY, 1);
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||
.addExpectedNodeWithOrder("FN",
|
||||
"AppropriatePrefix AppropriateGivenName "
|
||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
|
||||
if (isV30) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle"
|
||||
+ " AppropriatePhoneticFamily");
|
||||
final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem();
|
||||
if (VCardConfig.isVersion40(vcardType)) {
|
||||
final ContentValues contentValues = new ContentValues();
|
||||
contentValues.put("SORT-AS",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName");
|
||||
// vCard 4.0 (rev13) now uses SORT-AS parameter, which is not compatible with
|
||||
// either 2.1 nor 3.0.
|
||||
elem.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"),
|
||||
contentValues);
|
||||
} else {
|
||||
elem.addExpectedNodeWithOrder("N",
|
||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||
+ "AppropriatePrefix;AppropriateSuffix",
|
||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"));
|
||||
if (VCardConfig.isVersion30(vcardType)) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle"
|
||||
+ " AppropriatePhoneticFamily");
|
||||
}
|
||||
}
|
||||
|
||||
elem.addExpectedNodeWithOrder("FN",
|
||||
"AppropriatePrefix AppropriateGivenName "
|
||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
||||
}
|
||||
|
||||
public void testStructuredNameUseSuperPrimaryV21() {
|
||||
testStructuredNameUseSuperPrimaryCommon(V21);
|
||||
public void testStructuredNameComplicatedV21() {
|
||||
testStructuredNameComplicatedCommon(V21);
|
||||
}
|
||||
|
||||
public void testStructuredNameUseSuperPrimaryV30() {
|
||||
testStructuredNameUseSuperPrimaryCommon(V30);
|
||||
public void testStructuredNameComplicatedV30() {
|
||||
testStructuredNameComplicatedCommon(V30);
|
||||
}
|
||||
|
||||
public void testStructuredNameComplicatedV40() {
|
||||
testStructuredNameComplicatedCommon(V40);
|
||||
}
|
||||
|
||||
public void testNickNameV30() {
|
||||
@@ -258,6 +391,15 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
.addExpectedNodeWithOrder("NICKNAME", "Nicky");
|
||||
}
|
||||
|
||||
public void testNickNameV40() {
|
||||
mVerifier.initForExportTest(V40);
|
||||
mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
|
||||
.put(Nickname.NAME, "Nicky");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNodeWithOrder("NICKNAME", "Nicky");
|
||||
}
|
||||
|
||||
private void testPhoneBasicCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
@@ -275,6 +417,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPhoneBasicCommon(V30);
|
||||
}
|
||||
|
||||
public void testPhoneBasicV40() {
|
||||
testPhoneBasicCommon(V40);
|
||||
}
|
||||
|
||||
public void testPhoneRefrainFormatting() {
|
||||
mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING);
|
||||
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
@@ -367,6 +513,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPhoneVariousTypeSupport(V30);
|
||||
}
|
||||
|
||||
public void testPhoneVariousTypeSupportV40() {
|
||||
testPhoneVariousTypeSupport(V40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that "PREF"s are emitted appropriately.
|
||||
*/
|
||||
@@ -403,6 +553,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPhonePrefHandlingCommon(V30);
|
||||
}
|
||||
|
||||
public void testPhonePrefHandlingV40() {
|
||||
testPhonePrefHandlingCommon(V40);
|
||||
}
|
||||
|
||||
private void testMiscPhoneTypeHandling(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -438,7 +592,7 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "invalid");
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||
if (VCardConfig.isV30(vcardType)) {
|
||||
if (VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType)) {
|
||||
// vCard 3.0 accepts "invalid". Also stop using toUpper()
|
||||
elem.addExpectedNode("TEL", "1", new TypeSet("Modem"))
|
||||
.addExpectedNode("TEL", "2", new TypeSet("MSG"))
|
||||
@@ -468,6 +622,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testMiscPhoneTypeHandling(V30);
|
||||
}
|
||||
|
||||
public void testPhoneTypeHandlingV40() {
|
||||
testMiscPhoneTypeHandling(V40);
|
||||
}
|
||||
|
||||
private void testEmailBasicCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||
@@ -484,6 +642,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testEmailBasicCommon(V30);
|
||||
}
|
||||
|
||||
public void testEmailBasicV40() {
|
||||
testEmailBasicCommon(V40);
|
||||
}
|
||||
|
||||
private void testEmailVariousTypeSupportCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -514,6 +676,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testEmailVariousTypeSupportCommon(V30);
|
||||
}
|
||||
|
||||
public void testEmailVariousTypeSupportV40() {
|
||||
testEmailVariousTypeSupportCommon(V40);
|
||||
}
|
||||
|
||||
private void testEmailPrefHandlingCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -538,6 +704,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testEmailPrefHandlingCommon(V30);
|
||||
}
|
||||
|
||||
public void testEmailPrefHandlingV40() {
|
||||
testEmailPrefHandlingCommon(V40);
|
||||
}
|
||||
|
||||
private void testPostalAddressCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
@@ -569,6 +739,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPostalAddressCommon(V30);
|
||||
}
|
||||
|
||||
public void testPostalAddressV40() {
|
||||
testPostalAddressCommon(V40);
|
||||
}
|
||||
|
||||
private void testPostalAddressNonNeighborhood(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
@@ -586,6 +760,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPostalAddressNonNeighborhood(V30);
|
||||
}
|
||||
|
||||
public void testPostalAddressNonNeighborhoodV40() {
|
||||
testPostalAddressNonNeighborhood(V40);
|
||||
}
|
||||
|
||||
private void testPostalAddressNonCity(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
@@ -603,6 +781,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPostalAddressNonCity(V30);
|
||||
}
|
||||
|
||||
public void testPostalAddressNonCityV40() {
|
||||
testPostalAddressNonCity(V40);
|
||||
}
|
||||
|
||||
private void testPostalOnlyWithFormattedAddressCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
@@ -623,6 +805,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPostalOnlyWithFormattedAddressCommon(V30);
|
||||
}
|
||||
|
||||
public void testPostalOnlyWithFormattedAddressV40() {
|
||||
testPostalOnlyWithFormattedAddressCommon(V40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the vCard composer honors formatted data when it is available
|
||||
* even when it is partial.
|
||||
@@ -648,6 +834,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPostalWithBothStructuredAndFormattedCommon(V30);
|
||||
}
|
||||
|
||||
public void testPostalWithBothStructuredAndFormattedV40() {
|
||||
testPostalWithBothStructuredAndFormattedCommon(V40);
|
||||
}
|
||||
|
||||
private void testOrganizationCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -685,6 +875,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testOrganizationCommon(V30);
|
||||
}
|
||||
|
||||
public void testOrganizationV40() {
|
||||
testOrganizationCommon(V40);
|
||||
}
|
||||
|
||||
private void testImVariousTypeSupportCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -737,6 +931,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testImVariousTypeSupportCommon(V30);
|
||||
}
|
||||
|
||||
public void testImBasicV40() {
|
||||
testImVariousTypeSupportCommon(V40);
|
||||
}
|
||||
|
||||
private void testImPrefHandlingCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -762,6 +960,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testImPrefHandlingCommon(V30);
|
||||
}
|
||||
|
||||
public void testImPrefHandlingV40() {
|
||||
testImPrefHandlingCommon(V40);
|
||||
}
|
||||
|
||||
private void testWebsiteCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -786,6 +988,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testWebsiteCommon(V30);
|
||||
}
|
||||
|
||||
public void testWebsiteV40() {
|
||||
testWebsiteCommon(V40);
|
||||
}
|
||||
|
||||
private String getAndroidPropValue(final String mimeType, String value, Integer type) {
|
||||
return getAndroidPropValue(mimeType, value, type, null);
|
||||
}
|
||||
@@ -839,6 +1045,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testEventCommon(V30);
|
||||
}
|
||||
|
||||
public void testEventV40() {
|
||||
testEventCommon(V40);
|
||||
}
|
||||
|
||||
private void testNoteCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
@@ -860,8 +1070,13 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testNoteCommon(V30);
|
||||
}
|
||||
|
||||
public void testNoteV40() {
|
||||
testNoteCommon(V40);
|
||||
}
|
||||
|
||||
private void testPhotoCommon(int vcardType) {
|
||||
final boolean isV30 = vcardType == V30;
|
||||
final boolean useB =
|
||||
(VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType));
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
@@ -870,7 +1085,7 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
.put(Photo.PHOTO, sPhotoByteArray);
|
||||
|
||||
ContentValues contentValuesForPhoto = new ContentValues();
|
||||
contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64"));
|
||||
contentValuesForPhoto.put("ENCODING", (useB ? "b" : "BASE64"));
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("FN", "PhotoTest")
|
||||
.addExpectedNode("N", "PhotoTest;;;;",
|
||||
@@ -887,6 +1102,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
testPhotoCommon(V30);
|
||||
}
|
||||
|
||||
public void testPhotoV40() {
|
||||
testPhotoCommon(V40);
|
||||
}
|
||||
|
||||
private void testRelationCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE)
|
||||
@@ -957,18 +1176,22 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.IS_PRIMARY, 1); // Empty name. Should be ignored.
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "family1"); // Not primary. Should be ignored.
|
||||
.put(StructuredName.FAMILY_NAME, "family1") // Not primary. Should be ignored.
|
||||
.put(StructuredName.DISPLAY_NAME, "display");
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.IS_PRIMARY, 1)
|
||||
.put(StructuredName.FAMILY_NAME, "family2"); // This entry is what we want.
|
||||
.put(StructuredName.FAMILY_NAME, "family2") // This entry is what we want.
|
||||
.put(StructuredName.DISPLAY_NAME, "display");
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.IS_PRIMARY, 1)
|
||||
.put(StructuredName.FAMILY_NAME, "family3");
|
||||
.put(StructuredName.FAMILY_NAME, "family3")
|
||||
.put(StructuredName.DISPLAY_NAME, "display");
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "family4");
|
||||
.put(StructuredName.FAMILY_NAME, "family4")
|
||||
.put(StructuredName.DISPLAY_NAME, "display");
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("N", Arrays.asList("family2", "", "", "", ""))
|
||||
.addExpectedNode("FN", "family2");
|
||||
.addExpectedNode("FN", "display");
|
||||
}
|
||||
|
||||
public void testPickUpNonEmptyContentValuesV21() {
|
||||
@@ -978,4 +1201,23 @@ public class VCardExporterTests extends VCardTestsBase {
|
||||
public void testPickUpNonEmptyContentValuesV30() {
|
||||
testPickUpNonEmptyContentValuesCommon(V30);
|
||||
}
|
||||
|
||||
public void testPickUpNonEmptyContentValuesV40() {
|
||||
testPickUpNonEmptyContentValuesCommon(V40);
|
||||
}
|
||||
|
||||
public void testUseMultiByteTypeV30() {
|
||||
mVerifier.initForExportTest(V30);
|
||||
final ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||
.put(Phone.LABEL, "\u96FB\u8A71")
|
||||
.put(Phone.NUMBER, "1");
|
||||
mVerifier.addLineVerifierElem()
|
||||
.addExpected("N:")
|
||||
.addExpected("FN:")
|
||||
.addExpected("TEL;TYPE=\u96FB\u8A71:1");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
.addExpectedNode("TEL", "1", new TypeSet("\u96FB\u8A71"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
|
||||
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;
|
||||
@@ -27,9 +30,7 @@ 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 com.android.frameworks.coretests.R;
|
||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
||||
import android.provider.ContactsContract.Data;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -405,12 +406,12 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
|
||||
public void testV21SimpleCase1_Parsing() {
|
||||
mVerifier.initForImportTest(V21, R.raw.v21_simple_1);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
mVerifier.addPropertyNodesVerifierElemWithoutVersion() // no "VERSION:2.1" line.
|
||||
.addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""));
|
||||
}
|
||||
|
||||
public void testV21SimpleCase1_Type_Generic() {
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, R.raw.v21_simple_1);
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_simple_1);
|
||||
mVerifier.addContentValuesVerifierElem()
|
||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "Ando")
|
||||
@@ -419,7 +420,7 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testV21SimpleCase1_Type_Japanese() {
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_1);
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_1);
|
||||
mVerifier.addContentValuesVerifierElem()
|
||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "Ando")
|
||||
@@ -431,7 +432,7 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testV21SimpleCase2() {
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_2);
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_2);
|
||||
mVerifier.addContentValuesVerifierElem()
|
||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.DISPLAY_NAME, "Ando Roid");
|
||||
@@ -454,7 +455,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
public void testV21BackslashCase_Parsing() {
|
||||
mVerifier.initForImportTest(V21, R.raw.v21_backslash);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
||||
.addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;",
|
||||
Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""))
|
||||
.addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\");
|
||||
@@ -549,11 +549,11 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
public void testV21ComplicatedCase_Parsing() {
|
||||
mVerifier.initForImportTest(V21, R.raw.v21_complicated);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
||||
.addExpectedNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao",
|
||||
Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao"))
|
||||
.addExpectedNodeWithOrder("FN", "Joe Due")
|
||||
.addExpectedNodeWithOrder("ORG", "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper",
|
||||
.addExpectedNodeWithOrder("ORG",
|
||||
"Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper",
|
||||
Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper"))
|
||||
.addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!")
|
||||
.addExpectedNodeWithOrder("TITLE", "Shrimp Man")
|
||||
@@ -583,7 +583,8 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
.addExpectedNodeWithOrder("EMAIL", "forrestgump@walladalla.com",
|
||||
new TypeSet("PREF", "INTERNET"))
|
||||
.addExpectedNodeWithOrder("EMAIL", "cell@example.com", new TypeSet("CELL"))
|
||||
.addExpectedNodeWithOrder("NOTE", "The following note is the example from RFC 2045.")
|
||||
.addExpectedNodeWithOrder("NOTE",
|
||||
"The following note is the example from RFC 2045.")
|
||||
.addExpectedNodeWithOrder("NOTE",
|
||||
"Now's the time for all folk to come to the aid of their country.",
|
||||
null, null, mContentValuesForQP, null, null)
|
||||
@@ -677,12 +678,24 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
.put(Website.TYPE, Website.TYPE_HOMEPAGE);
|
||||
}
|
||||
|
||||
public void testInvalidMultipleLineV21() {
|
||||
mVerifier.initForImportTest(V21, R.raw.v21_invalid_multiple_line);
|
||||
ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.GIVEN_NAME, "Omega")
|
||||
.put(StructuredName.DISPLAY_NAME, "Omega");
|
||||
elem.addExpected(Email.CONTENT_ITEM_TYPE)
|
||||
.put(Email.TYPE, Email.TYPE_CUSTOM)
|
||||
.put(Email.LABEL, "INTERNET")
|
||||
.put(Email.ADDRESS, "\"Omega\" <omega@example.com>");
|
||||
}
|
||||
|
||||
public void testV30Simple_Parsing() {
|
||||
mVerifier.initForImportTest(V30, R.raw.v30_simple);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "3.0")
|
||||
.addExpectedNodeWithOrder("FN", "And Roid")
|
||||
.addExpectedNodeWithOrder("N", "And;Roid;;;", Arrays.asList("And", "Roid", "", "", ""))
|
||||
.addExpectedNodeWithOrder("N", "And;Roid;;;",
|
||||
Arrays.asList("And", "Roid", "", "", ""))
|
||||
.addExpectedNodeWithOrder("ORG", "Open;Handset; Alliance",
|
||||
Arrays.asList("Open", "Handset", " Alliance"))
|
||||
.addExpectedNodeWithOrder("SORT-STRING", "android")
|
||||
@@ -717,10 +730,8 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
// Though Japanese careers append ";;;;" at the end of the value of "SOUND",
|
||||
// vCard 2.1/3.0 specification does not allow multiple values.
|
||||
// Do not need to handle it as multiple values.
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
||||
R.raw.v21_japanese_1);
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_1);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1", null, null, null, null, null)
|
||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
|
||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
|
||||
null, mContentValuesForSJis, null, null)
|
||||
@@ -752,37 +763,35 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
|
||||
/**
|
||||
* Verifies vCard with Japanese can be parsed correctly with
|
||||
* {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC_UTF8}.
|
||||
* {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC}.
|
||||
*/
|
||||
public void testV21Japanese1_Type_Generic_Utf8() {
|
||||
testV21Japanese1Common(
|
||||
R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, false);
|
||||
R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies vCard with Japanese can be parsed correctly with
|
||||
* {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_SJIS}.
|
||||
* {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}.
|
||||
*/
|
||||
public void testV21Japanese1_Type_Japanese_Sjis() {
|
||||
testV21Japanese1Common(
|
||||
R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, true);
|
||||
R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies vCard with Japanese can be parsed correctly with
|
||||
* {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_UTF8}.
|
||||
* {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}.
|
||||
* since vCard 2.1 specifies the charset of each line if it contains non-Ascii.
|
||||
*/
|
||||
public void testV21Japanese1_Type_Japanese_Utf8() {
|
||||
testV21Japanese1Common(
|
||||
R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8, true);
|
||||
R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true);
|
||||
}
|
||||
|
||||
public void testV21Japanese2_Parsing() {
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
||||
R.raw.v21_japanese_2);
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_2);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
|
||||
Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031",
|
||||
"", "", ""),
|
||||
@@ -838,10 +847,8 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testV21MultipleEntryCase_Parse() {
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
||||
R.raw.v21_multiple_entry);
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
|
||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
|
||||
null, mContentValuesForSJis, null, null)
|
||||
@@ -854,7 +861,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
.addExpectedNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL"))
|
||||
.addExpectedNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME"));
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
|
||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
|
||||
null, mContentValuesForSJis, null, null)
|
||||
@@ -867,7 +873,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
.addExpectedNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY"))
|
||||
.addExpectedNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL"));
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
|
||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
|
||||
null, mContentValuesForSJis, null, null)
|
||||
@@ -882,8 +887,7 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testV21MultipleEntryCase() {
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
||||
R.raw.v21_multiple_entry);
|
||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry);
|
||||
ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033")
|
||||
@@ -957,7 +961,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
ContentValues contentValuesForValue = new ContentValues();
|
||||
contentValuesForValue.put("VALUE", "DATE");
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
||||
.addExpectedNodeWithOrder("N", Arrays.asList("Example", "", "", "", ""))
|
||||
.addExpectedNodeWithOrder("FN", "Example")
|
||||
.addExpectedNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue)
|
||||
@@ -974,6 +977,9 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "Example")
|
||||
.put(StructuredName.DISPLAY_NAME, "Example");
|
||||
elem.addExpected(Event.CONTENT_ITEM_TYPE)
|
||||
.put(Event.TYPE, Event.TYPE_ANNIVERSARY)
|
||||
.put(Event.START_DATE, "20091010");
|
||||
}
|
||||
|
||||
public void testTolerateInvalidCommentLikeLineV21() {
|
||||
@@ -988,16 +994,15 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testPagerV30_Parse() {
|
||||
mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
|
||||
mVerifier.initForImportTest(V30, R.raw.v30_pager);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "3.0")
|
||||
.addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
|
||||
.addExpectedNodeWithOrder("TEL", "6101231234@pagersample.com",
|
||||
new TypeSet("WORK", "MSG", "PAGER"));
|
||||
}
|
||||
|
||||
public void testPagerV30() {
|
||||
mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
|
||||
mVerifier.initForImportTest(V30, R.raw.v30_pager);
|
||||
ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "F")
|
||||
@@ -1012,7 +1017,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
public void testMultiBytePropV30_Parse() {
|
||||
mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("VERSION", "3.0")
|
||||
.addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
|
||||
.addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39"));
|
||||
}
|
||||
@@ -1030,4 +1034,72 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
.put(Phone.LABEL, "\u8D39")
|
||||
.put(Phone.NUMBER, "1");
|
||||
}
|
||||
|
||||
public void testCommaSeparatedParamsV30_Parse() {
|
||||
mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""),
|
||||
new TypeSet("PREF", "HOME"))
|
||||
.addExpectedNodeWithOrder("TEL", "1",
|
||||
new TypeSet("COMMA,SEPARATED:INSIDE.DQUOTE", "PREF"));
|
||||
}
|
||||
|
||||
public void testSortAsV40_Parse() {
|
||||
mVerifier.initForImportTest(V40, R.raw.v40_sort_as);
|
||||
|
||||
final ContentValues contentValuesForSortAsN = new ContentValues();
|
||||
contentValuesForSortAsN.put("SORT-AS",
|
||||
"\u3042\u3093\u3069\u3046;\u308D\u3044\u3069");
|
||||
final ContentValues contentValuesForSortAsOrg = new ContentValues();
|
||||
contentValuesForSortAsOrg.put("SORT-AS",
|
||||
"\u3050\u30FC\u3050\u308B;\u3051\u3093\u3055\u304F\u3076\u3082\u3093");
|
||||
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9")
|
||||
.addExpectedNodeWithOrder("N",
|
||||
Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9", "", "", ""),
|
||||
contentValuesForSortAsN)
|
||||
.addExpectedNodeWithOrder("ORG",
|
||||
Arrays.asList("\u30B0\u30FC\u30B0\u30EB", "\u691C\u7D22\u90E8\u9580"),
|
||||
contentValuesForSortAsOrg, new TypeSet("WORK"));
|
||||
}
|
||||
|
||||
public void testSortAsV40() {
|
||||
mVerifier.initForImportTest(V40, R.raw.v40_sort_as);
|
||||
final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4")
|
||||
.put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9")
|
||||
.put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9")
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3042\u3093\u3069\u3046")
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME,
|
||||
"\u308D\u3044\u3069");
|
||||
elem.addExpected(Organization.CONTENT_ITEM_TYPE)
|
||||
.put(Organization.TYPE, Organization.TYPE_WORK)
|
||||
.put(Organization.COMPANY, "\u30B0\u30FC\u30B0\u30EB")
|
||||
.put(Organization.DEPARTMENT, "\u691C\u7D22\u90E8\u9580")
|
||||
.put(Organization.PHONETIC_NAME,
|
||||
"\u3050\u30FC\u3050\u308B\u3051\u3093\u3055\u304F\u3076\u3082\u3093");
|
||||
}
|
||||
|
||||
public void testIMV21_Parse() {
|
||||
mVerifier.initForImportTest(V21, R.raw.v21_im);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("X-ANDROID-CUSTOM",
|
||||
Arrays.asList("vnd.android.cursor.item/nickname", "Nick", "1",
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "")) // 13
|
||||
.addExpectedNodeWithOrder("X-GOOGLE-TALK", "hhh@gmail.com");
|
||||
}
|
||||
|
||||
public void testIMV21() {
|
||||
mVerifier.initForImportTest(V21, R.raw.v21_im);
|
||||
final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
||||
elem.addExpected(Nickname.CONTENT_ITEM_TYPE)
|
||||
.put(Nickname.NAME, "Nick")
|
||||
.put(Nickname.TYPE, "1");
|
||||
elem.addExpected(Im.CONTENT_ITEM_TYPE)
|
||||
.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK)
|
||||
.put(Im.TYPE, Im.TYPE_HOME)
|
||||
.put(Im.DATA, "hhh@gmail.com");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,19 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Nickname;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Note;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
|
||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class VCardJapanizationTests extends VCardTestsBase {
|
||||
@@ -39,7 +36,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.put(StructuredName.PREFIX, "Dr.")
|
||||
.put(StructuredName.SUFFIX, "Ph.D");
|
||||
ContentValues contentValues =
|
||||
(VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8);
|
||||
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null);
|
||||
mVerifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
|
||||
contentValues)
|
||||
@@ -50,15 +47,15 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testNameUtf8V21() {
|
||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
|
||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE);
|
||||
}
|
||||
|
||||
public void testNameUtf8V30() {
|
||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
|
||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE);
|
||||
}
|
||||
|
||||
public void testNameShiftJis() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
||||
@@ -80,7 +77,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
* DoCoMo phones require all name elements should be in "family name" field.
|
||||
*/
|
||||
public void testNameDoCoMo() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
||||
@@ -105,8 +102,8 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.addExpectedNode("X-DCM-HMN-MODE", "");
|
||||
}
|
||||
|
||||
private void testPhoneticNameCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
private void testPhoneticNameCommon(int vcardType, String charset) {
|
||||
mVerifier.initForExportTest(vcardType, charset);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||
@@ -114,10 +111,10 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
|
||||
|
||||
final ContentValues contentValues =
|
||||
(VCardConfig.usesShiftJis(vcardType) ?
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
||||
mContentValuesForQPAndSJis) :
|
||||
(VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8));
|
||||
("SHIFT_JIS".equalsIgnoreCase(charset) ?
|
||||
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis :
|
||||
mContentValuesForSJis) :
|
||||
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null));
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||
elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060",
|
||||
contentValues)
|
||||
@@ -126,7 +123,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
contentValues)
|
||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046",
|
||||
contentValues);
|
||||
if (VCardConfig.isV30(vcardType)) {
|
||||
if (!VCardConfig.isVersion21(vcardType)) {
|
||||
elem.addExpectedNode("SORT-STRING",
|
||||
"\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046",
|
||||
contentValues);
|
||||
@@ -142,23 +139,23 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV21Utf8() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, null);
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV21Sjis() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV30Utf8() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null);
|
||||
}
|
||||
|
||||
public void testPhoneticNameForJapaneseV30SJis() {
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
|
||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
|
||||
}
|
||||
|
||||
public void testPhoneticNameForMobileV21_1() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||
@@ -182,7 +179,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testPhoneticNameForMobileV21_2() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||
@@ -198,8 +195,8 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73");
|
||||
}
|
||||
|
||||
private void testPostalAddressWithJapaneseCommon(int vcardType) {
|
||||
mVerifier.initForExportTest(vcardType);
|
||||
private void testPostalAddressWithJapaneseCommon(int vcardType, String charset) {
|
||||
mVerifier.initForExportTest(vcardType, charset);
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
|
||||
@@ -214,11 +211,11 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
.put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A");
|
||||
|
||||
ContentValues contentValues = (VCardConfig.usesShiftJis(vcardType) ?
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
||||
mContentValuesForQPAndSJis) :
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 :
|
||||
mContentValuesForQPAndUtf8));
|
||||
ContentValues contentValues = ("UTF-8".equalsIgnoreCase(charset) ?
|
||||
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis :
|
||||
mContentValuesForSJis) :
|
||||
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 :
|
||||
mContentValuesForUtf8));
|
||||
|
||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||
// LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is
|
||||
@@ -240,7 +237,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
|
||||
}
|
||||
public void testPostalAddresswithJapaneseV21() {
|
||||
testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
|
||||
testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,7 +245,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
* Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM
|
||||
*/
|
||||
public void testPostalAdrressForDoCoMo_1() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
|
||||
@@ -276,7 +273,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testPostalAdrressForDoCoMo_2() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
|
||||
@@ -301,7 +298,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testPostalAdrressForDoCoMo_3() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
@@ -329,7 +326,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
* Verifies the vCard exporter tolerates null TYPE.
|
||||
*/
|
||||
public void testPostalAdrressForDoCoMo_4() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredPostal.POBOX, "1");
|
||||
@@ -371,15 +368,15 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testJapanesePhoneNumberV21_1() {
|
||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
|
||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE);
|
||||
}
|
||||
|
||||
public void testJapanesePhoneNumberV30() {
|
||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
|
||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE);
|
||||
}
|
||||
|
||||
public void testJapanesePhoneNumberDoCoMo() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||
.put(Phone.NUMBER, "0312341234")
|
||||
@@ -399,7 +396,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testNoteDoCoMo() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||
ContactEntry entry = mVerifier.addInputEntry();
|
||||
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
||||
.put(Note.NOTE, "note1");
|
||||
@@ -421,7 +418,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
}
|
||||
|
||||
public void testAndroidCustomV21() {
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
|
||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC);
|
||||
mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
|
||||
.put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC");
|
||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.test.InstrumentationTestRunner;
|
||||
import android.test.InstrumentationTestSuite;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Usage: adb shell am instrument -w com.android.vcard.tests/.VCardTestRunnerTestRunner
|
||||
*/
|
||||
public class VCardTestRunner extends InstrumentationTestRunner {
|
||||
@Override
|
||||
public TestSuite getAllTests() {
|
||||
TestSuite suite = new InstrumentationTestSuite(this);
|
||||
suite.addTestSuite(VCardUtilsTests.class);
|
||||
suite.addTestSuite(VCardTestUtilsTests.class);
|
||||
suite.addTestSuite(VCardImporterTests.class);
|
||||
suite.addTestSuite(VCardExporterTests.class);
|
||||
suite.addTestSuite(VCardJapanizationTests.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getLoader() {
|
||||
return VCardTestRunner.class.getClassLoader();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests confirming utilities for vCard tests work fine.
|
||||
*
|
||||
* Now that the foundation classes for vCard test cases became too complicated to
|
||||
* rely on without testing itself.
|
||||
*/
|
||||
public class VCardTestUtilsTests extends AndroidTestCase {
|
||||
public void testShouldFailAtPropertyNodeVerification() {
|
||||
boolean failureDetected = false;
|
||||
try {
|
||||
final VCardVerifier verifier = new VCardVerifier(this);
|
||||
verifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_backslash);
|
||||
verifier.addPropertyNodesVerifierElem()
|
||||
.addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;--", // wrong
|
||||
Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""))
|
||||
.addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\");
|
||||
verifier.verify();
|
||||
} catch (AssertionFailedError e) {
|
||||
failureDetected = true;
|
||||
}
|
||||
if (!failureDetected) {
|
||||
TestCase.fail("Test case that should fail actually succeeded.");
|
||||
}
|
||||
}
|
||||
|
||||
public void testShouldFailAtContentValueVerification() {
|
||||
boolean failureDetected = false;
|
||||
try {
|
||||
final VCardVerifier verifier = new VCardVerifier(this);
|
||||
verifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_backslash);
|
||||
verifier.addContentValuesVerifierElem()
|
||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.GIVEN_NAME, "A;B\\")
|
||||
.put(StructuredName.MIDDLE_NAME, "C\\;")
|
||||
.put(StructuredName.PREFIX, "D")
|
||||
.put(StructuredName.SUFFIX, ":E");
|
||||
// DISPLAY_NAME is missing.
|
||||
verifier.verify();
|
||||
} catch (AssertionFailedError e) {
|
||||
failureDetected = true;
|
||||
}
|
||||
if (!failureDetected) {
|
||||
TestCase.fail("Test case that should fail actually succeeded.");
|
||||
}
|
||||
}
|
||||
|
||||
public void testShouldFailAtLineVerification() {
|
||||
boolean failureDetected = false;
|
||||
try {
|
||||
final VCardVerifier verifier = new VCardVerifier(this);
|
||||
verifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_GENERIC);
|
||||
verifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "\\")
|
||||
.put(StructuredName.GIVEN_NAME, ";")
|
||||
.put(StructuredName.MIDDLE_NAME, ",")
|
||||
.put(StructuredName.PREFIX, "\n")
|
||||
.put(StructuredName.DISPLAY_NAME, "[<{Unescaped:Asciis}>]");
|
||||
verifier.addLineVerifierElem()
|
||||
.addExpected("TEL:1") // wrong
|
||||
.addExpected("FN:[<{Unescaped:Asciis}>]");
|
||||
verifier.verify();
|
||||
} catch (AssertionFailedError e) {
|
||||
failureDetected = true;
|
||||
}
|
||||
if (!failureDetected) {
|
||||
TestCase.fail("Test case that should fail actually succeeded.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,10 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.pim.vcard.VCardUtils;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -85,31 +84,35 @@ public class VCardUtilsTests extends TestCase {
|
||||
|
||||
public void testToStringAvailableAsV30ParamValue() {
|
||||
// Smoke tests.
|
||||
assertEquals("HOME", VCardUtils.toStringAvailableAsV30ParameValue("HOME"));
|
||||
assertEquals("TEL", VCardUtils.toStringAvailableAsV30ParameValue("TEL"));
|
||||
assertEquals("PAGER", VCardUtils.toStringAvailableAsV30ParameValue("PAGER"));
|
||||
assertEquals("HOME", VCardUtils.toStringAsV30ParamValue("HOME"));
|
||||
assertEquals("TEL", VCardUtils.toStringAsV30ParamValue("TEL"));
|
||||
assertEquals("PAGER", VCardUtils.toStringAsV30ParamValue("PAGER"));
|
||||
|
||||
assertEquals("\"\"", VCardUtils.toStringAvailableAsV30ParameValue(""));
|
||||
assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue("")));
|
||||
assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue(null)));
|
||||
assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue(" \t")));
|
||||
|
||||
// non-Ascii must be allowed
|
||||
assertEquals("\u4E8B\u52D9\u6240",
|
||||
VCardUtils.toStringAvailableAsV30ParameValue("\u4E8B\u52D9\u6240"));
|
||||
VCardUtils.toStringAsV30ParamValue("\u4E8B\u52D9\u6240"));
|
||||
// Reported as bug report.
|
||||
assertEquals("\u8D39", VCardUtils.toStringAvailableAsV30ParameValue("\u8D39"));
|
||||
assertEquals("\u8D39", VCardUtils.toStringAsV30ParamValue("\u8D39"));
|
||||
assertEquals("\"comma,separated\"",
|
||||
VCardUtils.toStringAvailableAsV30ParameValue("comma,separated"));
|
||||
VCardUtils.toStringAsV30ParamValue("comma,separated"));
|
||||
assertEquals("\"colon:aware\"",
|
||||
VCardUtils.toStringAvailableAsV30ParameValue("colon:aware"));
|
||||
VCardUtils.toStringAsV30ParamValue("colon:aware"));
|
||||
// CTL characters.
|
||||
assertEquals("CTLExample",
|
||||
VCardUtils.toStringAvailableAsV30ParameValue("CTL\u0001Example"));
|
||||
VCardUtils.toStringAsV30ParamValue("CTL\u0001Example"));
|
||||
assertTrue(TextUtils.isEmpty(
|
||||
VCardUtils.toStringAsV30ParamValue("\u0001\u0002\u0003")));
|
||||
// DQUOTE must be removed.
|
||||
assertEquals("quoted",
|
||||
VCardUtils.toStringAvailableAsV30ParameValue("\"quoted\""));
|
||||
VCardUtils.toStringAsV30ParamValue("\"quoted\""));
|
||||
// DQUOTE must be removed basically, but we should detect a space, which
|
||||
// require us to use DQUOTE again.
|
||||
// Right-side has one more illegal dquote to test quote-handle code thoroughly.
|
||||
assertEquals("\"Already quoted\"",
|
||||
VCardUtils.toStringAvailableAsV30ParameValue("\"Already quoted\"\""));
|
||||
VCardUtils.toStringAsV30ParamValue("\"Already quoted\"\""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.provider.ContactsContract.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class representing one contact, which should contain multiple ContentValues like
|
||||
* StructuredName, Email, etc.
|
||||
* </p>
|
||||
*/
|
||||
public final class ContactEntry {
|
||||
private final List<ContentValues> mContentValuesList = new ArrayList<ContentValues>();
|
||||
|
||||
public ContentValuesBuilder addContentValues(String mimeType) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(Data.MIMETYPE, mimeType);
|
||||
mContentValuesList.add(contentValues);
|
||||
return new ContentValuesBuilder(contentValues);
|
||||
}
|
||||
|
||||
public List<ContentValues> getList() {
|
||||
return mContentValuesList;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
@@ -22,7 +21,7 @@ import android.content.ContentValues;
|
||||
* ContentValues-like class which enables users to chain put() methods and restricts
|
||||
* the other methods.
|
||||
*/
|
||||
/* package */ class ContentValuesBuilder {
|
||||
public class ContentValuesBuilder {
|
||||
private final ContentValues mContentValues;
|
||||
|
||||
public ContentValuesBuilder(final ContentValues contentValues) {
|
||||
@@ -34,6 +33,7 @@ import android.content.ContentValues;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
public ContentValuesBuilder put(String key, Byte value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
@@ -42,13 +42,14 @@ import android.content.ContentValues;
|
||||
public ContentValuesBuilder put(String key, Short value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
}*/
|
||||
|
||||
public ContentValuesBuilder put(String key, Integer value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
public ContentValuesBuilder put(String key, Long value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
@@ -67,7 +68,7 @@ import android.content.ContentValues;
|
||||
public ContentValuesBuilder put(String key, Boolean value) {
|
||||
mContentValues.put(key, value);
|
||||
return this;
|
||||
}
|
||||
}*/
|
||||
|
||||
public ContentValuesBuilder put(String key, byte[] value) {
|
||||
mContentValues.put(key, value);
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.pim.vcard.VCardEntry;
|
||||
import android.pim.vcard.VCardEntryHandler;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ContentValuesVerifier implements VCardEntryHandler {
|
||||
private List<ContentValuesVerifierElem> mContentValuesVerifierElemList =
|
||||
new ArrayList<ContentValuesVerifierElem>();
|
||||
private int mIndex;
|
||||
|
||||
public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) {
|
||||
ContentValuesVerifierElem elem = new ContentValuesVerifierElem(androidTestCase);
|
||||
mContentValuesVerifierElemList.add(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
|
||||
elem.onParsingStart();
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryCreated(VCardEntry entry) {
|
||||
AndroidTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size());
|
||||
mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry);
|
||||
mIndex++;
|
||||
}
|
||||
|
||||
public void onEnd() {
|
||||
for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
|
||||
elem.onParsingEnd();
|
||||
elem.verifyResolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.VCardEntry;
|
||||
import android.pim.vcard.VCardEntryCommitter;
|
||||
import android.pim.vcard.VCardEntryConstructor;
|
||||
import android.pim.vcard.VCardEntryHandler;
|
||||
import android.pim.vcard.VCardParser;
|
||||
import android.pim.vcard.VCardParser_V21;
|
||||
import android.pim.vcard.VCardParser_V30;
|
||||
import android.pim.vcard.VCardUtils;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.test.AndroidTestCase;
|
||||
@@ -31,7 +29,7 @@ import android.test.AndroidTestCase;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/* package */ class ContentValuesVerifierElem {
|
||||
public class ContentValuesVerifierElem {
|
||||
private final AndroidTestCase mTestCase;
|
||||
private final ImportTestResolver mResolver;
|
||||
private final VCardEntryHandler mHandler;
|
||||
@@ -49,20 +47,14 @@ import java.io.InputStream;
|
||||
return new ContentValuesBuilder(contentValues);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType)
|
||||
public void verify(int resId, int vcardType)
|
||||
throws IOException, VCardException {
|
||||
verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType);
|
||||
verify(mTestCase.getContext().getResources().openRawResource(resId), vcardType);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
vCardParser = new VCardParser_V30(true); // use StrictParsing
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
VCardEntryConstructor builder =
|
||||
new VCardEntryConstructor(null, null, false, vCardType, null);
|
||||
public void verify(InputStream is, int vcardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser = VCardUtils.getAppropriateParser(vcardType);
|
||||
final VCardEntryConstructor builder = new VCardEntryConstructor(vcardType, null);
|
||||
builder.addEntryHandler(mHandler);
|
||||
try {
|
||||
vCardParser.parse(is, builder);
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
@@ -13,7 +13,7 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
@@ -21,11 +21,12 @@ import android.content.Entity;
|
||||
import android.content.EntityIterator;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.pim.vcard.VCardComposer;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContentProvider;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.test.mock.MockCursor;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -34,82 +35,44 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/* package */ public class ExportTestResolver extends MockContentResolver {
|
||||
ExportTestProvider mProvider;
|
||||
public ExportTestResolver(TestCase testCase) {
|
||||
mProvider = new ExportTestProvider(testCase);
|
||||
addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
|
||||
addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
|
||||
}
|
||||
|
||||
public ContactEntry addInputContactEntry() {
|
||||
return mProvider.buildInputEntry();
|
||||
}
|
||||
|
||||
public ExportTestProvider getProvider() {
|
||||
return mProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ class MockEntityIterator implements EntityIterator {
|
||||
List<Entity> mEntityList;
|
||||
Iterator<Entity> mIterator;
|
||||
|
||||
public MockEntityIterator(List<ContentValues> contentValuesList) {
|
||||
mEntityList = new ArrayList<Entity>();
|
||||
Entity entity = new Entity(new ContentValues());
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
entity.addSubValue(Data.CONTENT_URI, contentValues);
|
||||
}
|
||||
mEntityList.add(entity);
|
||||
mIterator = mEntityList.iterator();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return mIterator.hasNext();
|
||||
}
|
||||
|
||||
public Entity next() {
|
||||
return mIterator.next();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove not supported");
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mIterator = mEntityList.iterator();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents one contact, which should contain multiple ContentValues like
|
||||
* StructuredName, Email, etc.
|
||||
*/
|
||||
/* package */ class ContactEntry {
|
||||
private final List<ContentValues> mContentValuesList = new ArrayList<ContentValues>();
|
||||
|
||||
public ContentValuesBuilder addContentValues(String mimeType) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(Data.MIMETYPE, mimeType);
|
||||
mContentValuesList.add(contentValues);
|
||||
return new ContentValuesBuilder(contentValues);
|
||||
}
|
||||
|
||||
public List<ContentValues> getList() {
|
||||
return mContentValuesList;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ class ExportTestProvider extends MockContentProvider {
|
||||
final private TestCase mTestCase;
|
||||
public class ExportTestProvider extends MockContentProvider {
|
||||
final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
|
||||
|
||||
public ExportTestProvider(TestCase testCase) {
|
||||
mTestCase = testCase;
|
||||
private static class MockEntityIterator implements EntityIterator {
|
||||
List<Entity> mEntityList;
|
||||
Iterator<Entity> mIterator;
|
||||
|
||||
public MockEntityIterator(List<ContentValues> contentValuesList) {
|
||||
mEntityList = new ArrayList<Entity>();
|
||||
Entity entity = new Entity(new ContentValues());
|
||||
for (ContentValues contentValues : contentValuesList) {
|
||||
entity.addSubValue(Data.CONTENT_URI, contentValues);
|
||||
}
|
||||
mEntityList.add(entity);
|
||||
mIterator = mEntityList.iterator();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return mIterator.hasNext();
|
||||
}
|
||||
|
||||
public Entity next() {
|
||||
return mIterator.next();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove not supported");
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mIterator = mEntityList.iterator();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
public ExportTestProvider(AndroidTestCase androidTestCase) {
|
||||
}
|
||||
|
||||
public ContactEntry buildInputEntry() {
|
||||
@@ -126,27 +89,17 @@ import java.util.List;
|
||||
* We still keep using this method since we don't have a propeer way to know
|
||||
* which value in the ContentValue corresponds to the entry in Contacts database.
|
||||
* </p>
|
||||
* <p>
|
||||
* Detail:
|
||||
* There's an easy way to know which index "family name" corresponds to, via
|
||||
* {@link android.provider.ContactsContract}.
|
||||
* FAMILY_NAME equals DATA3, so the corresponding index
|
||||
* for "family name" should be 2 (note that index is 0-origin).
|
||||
* However, we cannot know what the index 2 corresponds to; it may be "family name",
|
||||
* "label" for now, but may be the other some column in the future. We don't have
|
||||
* convenient way to know the original data structure.
|
||||
* </p>
|
||||
*/
|
||||
public EntityIterator queryEntities(Uri uri,
|
||||
String selection, String[] selectionArgs, String sortOrder) {
|
||||
mTestCase.assertTrue(uri != null);
|
||||
mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
|
||||
TestCase.assertTrue(uri != null);
|
||||
TestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
|
||||
final String authority = uri.getAuthority();
|
||||
mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority));
|
||||
mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection));
|
||||
mTestCase.assertEquals(1, selectionArgs.length);
|
||||
TestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority));
|
||||
TestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection));
|
||||
TestCase.assertEquals(1, selectionArgs.length);
|
||||
final int id = Integer.parseInt(selectionArgs[0]);
|
||||
mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size());
|
||||
TestCase.assertTrue(id >= 0 && id < mContactEntryList.size());
|
||||
|
||||
return new MockEntityIterator(mContactEntryList.get(id).getList());
|
||||
}
|
||||
@@ -154,11 +107,11 @@ import java.util.List;
|
||||
@Override
|
||||
public Cursor query(Uri uri,String[] projection,
|
||||
String selection, String[] selectionArgs, String sortOrder) {
|
||||
mTestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri));
|
||||
TestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri));
|
||||
// In this test, following arguments are not supported.
|
||||
mTestCase.assertNull(selection);
|
||||
mTestCase.assertNull(selectionArgs);
|
||||
mTestCase.assertNull(sortOrder);
|
||||
TestCase.assertNull(selection);
|
||||
TestCase.assertNull(selectionArgs);
|
||||
TestCase.assertNull(sortOrder);
|
||||
|
||||
return new MockCursor() {
|
||||
int mCurrentPosition = -1;
|
||||
@@ -196,14 +149,14 @@ import java.util.List;
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String columnName) {
|
||||
mTestCase.assertEquals(Contacts._ID, columnName);
|
||||
TestCase.assertEquals(Contacts._ID, columnName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) {
|
||||
mTestCase.assertEquals(0, columnIndex);
|
||||
mTestCase.assertTrue(mCurrentPosition >= 0
|
||||
TestCase.assertEquals(0, columnIndex);
|
||||
TestCase.assertTrue(mCurrentPosition >= 0
|
||||
&& mCurrentPosition < mContactEntryList.size());
|
||||
return mCurrentPosition;
|
||||
}
|
||||
@@ -218,4 +171,4 @@ import java.util.List;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.pim.vcard.VCardComposer;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
public class ExportTestResolver extends MockContentResolver {
|
||||
private final ExportTestProvider mProvider;
|
||||
public ExportTestResolver(AndroidTestCase androidTestCase) {
|
||||
mProvider = new ExportTestProvider(androidTestCase);
|
||||
addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
|
||||
addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
|
||||
}
|
||||
|
||||
public ContactEntry addInputContactEntry() {
|
||||
return mProvider.buildInputEntry();
|
||||
}
|
||||
|
||||
public ExportTestProvider getProvider() {
|
||||
return mProvider;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
@@ -34,8 +34,8 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContentProvider;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -51,38 +51,7 @@ import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/* package */ class ImportTestResolver extends MockContentResolver {
|
||||
final ImportTestProvider mProvider;
|
||||
|
||||
public ImportTestResolver(TestCase testCase) {
|
||||
mProvider = new ImportTestProvider(testCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentProviderResult[] applyBatch(String authority,
|
||||
ArrayList<ContentProviderOperation> operations) {
|
||||
equalsString(authority, RawContacts.CONTENT_URI.toString());
|
||||
return mProvider.applyBatch(operations);
|
||||
}
|
||||
|
||||
public void addExpectedContentValues(ContentValues expectedContentValues) {
|
||||
mProvider.addExpectedContentValues(expectedContentValues);
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
mProvider.verify();
|
||||
}
|
||||
|
||||
private static boolean equalsString(String a, String b) {
|
||||
if (a == null || a.length() == 0) {
|
||||
return b == null || b.length() == 0;
|
||||
} else {
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ class ImportTestProvider extends MockContentProvider {
|
||||
public class ImportTestProvider extends MockContentProvider {
|
||||
private static final Set<String> sKnownMimeTypeSet =
|
||||
new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
|
||||
Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE,
|
||||
@@ -95,10 +64,7 @@ import java.util.TreeMap;
|
||||
|
||||
final Map<String, Collection<ContentValues>> mMimeTypeToExpectedContentValues;
|
||||
|
||||
private final TestCase mTestCase;
|
||||
|
||||
public ImportTestProvider(TestCase testCase) {
|
||||
mTestCase = testCase;
|
||||
public ImportTestProvider(AndroidTestCase androidTestCase) {
|
||||
mMimeTypeToExpectedContentValues =
|
||||
new HashMap<String, Collection<ContentValues>>();
|
||||
for (String acceptanbleMimeType : sKnownMimeTypeSet) {
|
||||
@@ -113,7 +79,7 @@ import java.util.TreeMap;
|
||||
public void addExpectedContentValues(ContentValues expectedContentValues) {
|
||||
final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE);
|
||||
if (!sKnownMimeTypeSet.contains(mimeType)) {
|
||||
mTestCase.fail(String.format(
|
||||
TestCase.fail(String.format(
|
||||
"Unknow MimeType %s in the test code. Test code should be broken.",
|
||||
mimeType));
|
||||
}
|
||||
@@ -127,7 +93,7 @@ import java.util.TreeMap;
|
||||
public ContentProviderResult[] applyBatch(
|
||||
ArrayList<ContentProviderOperation> operations) {
|
||||
if (operations == null) {
|
||||
mTestCase.fail("There is no operation.");
|
||||
TestCase.fail("There is no operation.");
|
||||
}
|
||||
|
||||
final int size = operations.size();
|
||||
@@ -148,12 +114,12 @@ import java.util.TreeMap;
|
||||
fakeResultArray, i);
|
||||
final Uri uri = operation.getUri();
|
||||
if (uri.equals(RawContacts.CONTENT_URI)) {
|
||||
mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
|
||||
mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
|
||||
TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
|
||||
TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
|
||||
} else if (uri.equals(Data.CONTENT_URI)) {
|
||||
final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
|
||||
if (!sKnownMimeTypeSet.contains(mimeType)) {
|
||||
mTestCase.fail(String.format(
|
||||
TestCase.fail(String.format(
|
||||
"Unknown MimeType %s. Probably added after developing this test",
|
||||
mimeType));
|
||||
}
|
||||
@@ -185,7 +151,7 @@ import java.util.TreeMap;
|
||||
final Collection<ContentValues> contentValuesCollection =
|
||||
mMimeTypeToExpectedContentValues.get(mimeType);
|
||||
if (contentValuesCollection.isEmpty()) {
|
||||
mTestCase.fail("ContentValues for MimeType " + mimeType
|
||||
TestCase.fail("ContentValues for MimeType " + mimeType
|
||||
+ " is not expected at all (" + actualContentValues + ")");
|
||||
}
|
||||
boolean checked = false;
|
||||
@@ -197,23 +163,25 @@ import java.util.TreeMap;
|
||||
+ convertToEasilyReadableString(actualContentValues));*/
|
||||
if (equalsForContentValues(expectedContentValues,
|
||||
actualContentValues)) {
|
||||
mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
|
||||
TestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
|
||||
checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!checked) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("\n");
|
||||
builder.append("Unexpected: ");
|
||||
builder.append(convertToEasilyReadableString(actualContentValues));
|
||||
builder.append("\nExpected: ");
|
||||
builder.append("\n");
|
||||
builder.append("Expected : ");
|
||||
for (ContentValues expectedContentValues : contentValuesCollection) {
|
||||
builder.append(convertToEasilyReadableString(expectedContentValues));
|
||||
}
|
||||
mTestCase.fail(builder.toString());
|
||||
TestCase.fail(builder.toString());
|
||||
}
|
||||
} else {
|
||||
mTestCase.fail("Unexpected Uri has come: " + uri);
|
||||
TestCase.fail("Unexpected Uri has come: " + uri);
|
||||
}
|
||||
} // for (int i = 0; i < size; i++) {
|
||||
return fakeResultArray;
|
||||
@@ -232,7 +200,7 @@ import java.util.TreeMap;
|
||||
final String failMsg =
|
||||
"There is(are) remaining expected ContentValues instance(s): \n"
|
||||
+ builder.toString();
|
||||
mTestCase.fail(failMsg);
|
||||
TestCase.fail(failMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +220,7 @@ import java.util.TreeMap;
|
||||
if (Data.MIMETYPE.equals(key)) {
|
||||
mimeTypeValue = valueString;
|
||||
} else {
|
||||
mTestCase.assertNotNull(key);
|
||||
TestCase.assertNotNull(key);
|
||||
sortedMap.put(key, valueString);
|
||||
}
|
||||
}
|
||||
@@ -273,7 +241,7 @@ import java.util.TreeMap;
|
||||
}
|
||||
|
||||
private static boolean equalsForContentValues(
|
||||
ContentValues expected, ContentValues actual) {
|
||||
final ContentValues expected, final ContentValues actual) {
|
||||
if (expected == actual) {
|
||||
return true;
|
||||
} else if (expected == null || actual == null || expected.size() != actual.size()) {
|
||||
@@ -286,15 +254,21 @@ import java.util.TreeMap;
|
||||
if (!actual.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
// Type mismatch usuall happens as importer doesn't care the type of each value.
|
||||
// For example, variable type might be Integer when importing the type of TEL,
|
||||
// while variable type would be String when importing the type of RELATION.
|
||||
final Object actualValue = actual.get(key);
|
||||
if (value instanceof byte[]) {
|
||||
Object actualValue = actual.get(key);
|
||||
if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
|
||||
byte[] e = (byte[])value;
|
||||
byte[] a = (byte[])actualValue;
|
||||
return false;
|
||||
}
|
||||
} else if (!value.equals(actual.get(key))) {
|
||||
return false;
|
||||
} else if (!value.equals(actualValue) &&
|
||||
!value.toString().equals(actualValue.toString())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentValues;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ImportTestResolver extends MockContentResolver {
|
||||
private final ImportTestProvider mProvider;
|
||||
|
||||
public ImportTestResolver(AndroidTestCase androidTestCase) {
|
||||
mProvider = new ImportTestProvider(androidTestCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentProviderResult[] applyBatch(String authority,
|
||||
ArrayList<ContentProviderOperation> operations) {
|
||||
equalsString(authority, RawContacts.CONTENT_URI.toString());
|
||||
return mProvider.applyBatch(operations);
|
||||
}
|
||||
|
||||
public void addExpectedContentValues(ContentValues expectedContentValues) {
|
||||
mProvider.addExpectedContentValues(expectedContentValues);
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
mProvider.verify();
|
||||
}
|
||||
|
||||
private static boolean equalsString(String a, String b) {
|
||||
if (a == null || a.length() == 0) {
|
||||
return b == null || b.length() == 0;
|
||||
} else {
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,39 +13,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.pim.vcard.VCardComposer;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class LineVerifier implements VCardComposer.OneEntryHandler {
|
||||
private final TestCase mTestCase;
|
||||
public class LineVerifier implements VCardComposer.OneEntryHandler {
|
||||
private final AndroidTestCase mAndroidTestCase;
|
||||
private final ArrayList<LineVerifierElem> mLineVerifierElemList;
|
||||
private int mVCardType;
|
||||
private int index;
|
||||
|
||||
public LineVerifier(TestCase testCase, int vcardType) {
|
||||
mTestCase = testCase;
|
||||
public LineVerifier(AndroidTestCase androidTestCase, int vcardType) {
|
||||
mAndroidTestCase = androidTestCase;
|
||||
mLineVerifierElemList = new ArrayList<LineVerifierElem>();
|
||||
mVCardType = vcardType;
|
||||
}
|
||||
|
||||
public LineVerifierElem addLineVerifierElem() {
|
||||
LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType);
|
||||
LineVerifierElem lineVerifier = new LineVerifierElem(mAndroidTestCase, mVCardType);
|
||||
mLineVerifierElemList.add(lineVerifier);
|
||||
return lineVerifier;
|
||||
}
|
||||
|
||||
public void verify(String vcard) {
|
||||
if (index >= mLineVerifierElemList.size()) {
|
||||
mTestCase.fail("Insufficient number of LineVerifier (" + index + ")");
|
||||
TestCase.fail("Insufficient number of LineVerifier (" + index + ")");
|
||||
}
|
||||
|
||||
LineVerifierElem lineVerifier = mLineVerifierElemList.get(index);
|
||||
final LineVerifierElem lineVerifier = mLineVerifierElemList.get(index);
|
||||
lineVerifier.verify(vcard);
|
||||
|
||||
index++;
|
||||
@@ -13,9 +13,10 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -23,14 +24,12 @@ import junit.framework.TestCase;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class LineVerifierElem {
|
||||
private final TestCase mTestCase;
|
||||
public class LineVerifierElem {
|
||||
private final List<String> mExpectedLineList = new ArrayList<String>();
|
||||
private final boolean mIsV30;
|
||||
private final int mVCardType;
|
||||
|
||||
public LineVerifierElem(TestCase testCase, int vcardType) {
|
||||
mTestCase = testCase;
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
public LineVerifierElem(AndroidTestCase androidTestCase, int vcardType) {
|
||||
mVCardType = vcardType;
|
||||
}
|
||||
|
||||
public LineVerifierElem addExpected(final String line) {
|
||||
@@ -55,21 +54,23 @@ class LineVerifierElem {
|
||||
|
||||
if ("BEGIN:VCARD".equalsIgnoreCase(line)) {
|
||||
if (beginExists) {
|
||||
mTestCase.fail("Multiple \"BEGIN:VCARD\" line found");
|
||||
TestCase.fail("Multiple \"BEGIN:VCARD\" line found");
|
||||
} else {
|
||||
beginExists = true;
|
||||
continue;
|
||||
}
|
||||
} else if ("END:VCARD".equalsIgnoreCase(line)) {
|
||||
if (endExists) {
|
||||
mTestCase.fail("Multiple \"END:VCARD\" line found");
|
||||
TestCase.fail("Multiple \"END:VCARD\" line found");
|
||||
} else {
|
||||
endExists = true;
|
||||
continue;
|
||||
}
|
||||
} else if ((mIsV30 ? "VERSION:3.0" : "VERSION:2.1").equalsIgnoreCase(line)) {
|
||||
} else if ((VCardConfig.isVersion21(mVCardType) ? "VERSION:2.1" :
|
||||
(VCardConfig.isVersion30(mVCardType) ? "VERSION:3.0" :
|
||||
"VERSION:4.0")).equalsIgnoreCase(line)) {
|
||||
if (versionExists) {
|
||||
mTestCase.fail("Multiple VERSION line + found");
|
||||
TestCase.fail("Multiple VERSION line + found");
|
||||
} else {
|
||||
versionExists = true;
|
||||
continue;
|
||||
@@ -77,18 +78,16 @@ class LineVerifierElem {
|
||||
}
|
||||
|
||||
if (!beginExists) {
|
||||
mTestCase.fail("Property other than BEGIN came before BEGIN property: "
|
||||
+ line);
|
||||
TestCase.fail("Property other than BEGIN came before BEGIN property: " + line);
|
||||
} else if (endExists) {
|
||||
mTestCase.fail("Property other than END came after END property: "
|
||||
+ line);
|
||||
TestCase.fail("Property other than END came after END property: " + line);
|
||||
}
|
||||
|
||||
final int index = mExpectedLineList.indexOf(line);
|
||||
if (index >= 0) {
|
||||
mExpectedLineList.remove(index);
|
||||
} else {
|
||||
mTestCase.fail("Unexpected line: " + line);
|
||||
TestCase.fail("Unexpected line: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +98,7 @@ class LineVerifierElem {
|
||||
buffer.append("\n");
|
||||
}
|
||||
|
||||
mTestCase.fail("Expected line(s) not found:" + buffer.toString());
|
||||
TestCase.fail("Expected line(s) not found:" + buffer.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardEntry;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -26,12 +25,18 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class representing one property (e.g. "N;ENCODING=UTF-8:family:given:middle:prefix:suffix").
|
||||
* </p>
|
||||
* <p>
|
||||
* Previously used in main vCard handling code but now exists only for testing.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* Especially useful for testing parser code (VCardParser), since all properties can be
|
||||
* checked via this class unlike {@link VCardEntry}, which only emits the result of
|
||||
* interpretation of the content of each vCard. We cannot know whether vCard parser or
|
||||
* ContactStruct is wrong withouth this class.
|
||||
* {@link VCardEntry} is wrong without this class.
|
||||
* </p>
|
||||
*/
|
||||
public class PropertyNode {
|
||||
public String propName;
|
||||
@@ -43,7 +48,8 @@ public class PropertyNode {
|
||||
*/
|
||||
public byte[] propValue_bytes;
|
||||
|
||||
/** param store: key=paramType, value=paramValue
|
||||
/**
|
||||
* param store: key=paramType, value=paramValue
|
||||
* Note that currently PropertyNode class does not support multiple param-values
|
||||
* defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
|
||||
* one String value like "A,B", not ["A", "B"]...
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.pim.vcard.VCardParser;
|
||||
import android.pim.vcard.VCardUtils;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PropertyNodesVerifier extends VNodeBuilder {
|
||||
private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList;
|
||||
private final AndroidTestCase mAndroidTestCase;
|
||||
private int mIndex;
|
||||
|
||||
public PropertyNodesVerifier(AndroidTestCase testCase) {
|
||||
super();
|
||||
mPropertyNodesVerifierElemList = new ArrayList<PropertyNodesVerifierElem>();
|
||||
mAndroidTestCase = testCase;
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
|
||||
PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase);
|
||||
mPropertyNodesVerifierElemList.add(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
public void verify(int resId, int vcardType) throws IOException, VCardException {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vcardType);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vcardType, final VCardParser parser)
|
||||
throws IOException, VCardException {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
|
||||
vcardType, parser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vcardType) throws IOException, VCardException {
|
||||
final VCardParser parser = VCardUtils.getAppropriateParser(vcardType);
|
||||
verify(is, vcardType, parser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vcardType, final VCardParser parser)
|
||||
throws IOException, VCardException {
|
||||
try {
|
||||
parser.parse(is, this);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endEntry() {
|
||||
super.endEntry();
|
||||
AndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
|
||||
AndroidTestCase.assertTrue(mIndex < vNodeList.size());
|
||||
mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
|
||||
mIndex++;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,87 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.VCardParser;
|
||||
import android.pim.vcard.VCardParser_V21;
|
||||
import android.pim.vcard.VCardParser_V30;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/* package */ class PropertyNodesVerifier extends VNodeBuilder {
|
||||
private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList;
|
||||
private final AndroidTestCase mAndroidTestCase;
|
||||
private int mIndex;
|
||||
|
||||
public PropertyNodesVerifier(AndroidTestCase testCase) {
|
||||
mPropertyNodesVerifierElemList = new ArrayList<PropertyNodesVerifierElem>();
|
||||
mAndroidTestCase = testCase;
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
|
||||
PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase);
|
||||
mPropertyNodesVerifierElemList.add(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType)
|
||||
throws IOException, VCardException {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
|
||||
vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
vCardParser = new VCardParser_V30(true); // Use StrictParsing.
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
verify(is, vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
try {
|
||||
vCardParser.parse(is, this);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endEntry() {
|
||||
super.endEntry();
|
||||
mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
|
||||
mAndroidTestCase.assertTrue(mIndex < vNodeList.size());
|
||||
mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
|
||||
mIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class which verifies input VNode.
|
||||
*
|
||||
@@ -102,7 +34,7 @@ import java.util.List;
|
||||
* If the node does not exist in the "ordered list", the class refers to
|
||||
* "unorderd expected property set" and checks the node is expected somewhere.
|
||||
*/
|
||||
/* package */ class PropertyNodesVerifierElem {
|
||||
public class PropertyNodesVerifierElem {
|
||||
public static class TypeSet extends HashSet<String> {
|
||||
public TypeSet(String ... array) {
|
||||
super(Arrays.asList(array));
|
||||
@@ -119,12 +51,10 @@ import java.util.List;
|
||||
// Intentionally use ArrayList instead of Set, assuming there may be more than one
|
||||
// exactly same objects.
|
||||
private final ArrayList<PropertyNode> mUnorderedNodeList;
|
||||
private final TestCase mTestCase;
|
||||
|
||||
public PropertyNodesVerifierElem(TestCase testCase) {
|
||||
public PropertyNodesVerifierElem(AndroidTestCase androidTestCase) {
|
||||
mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
|
||||
mUnorderedNodeList = new ArrayList<PropertyNode>();
|
||||
mTestCase = testCase;
|
||||
}
|
||||
|
||||
// WithOrder
|
||||
@@ -170,6 +100,12 @@ import java.util.List;
|
||||
paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName,
|
||||
List<String> propValueList, ContentValues paramMap, TypeSet paramMap_TYPE) {
|
||||
return addExpectedNodeWithOrder(propName, null, propValueList, null, paramMap,
|
||||
paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||
ContentValues paramMap, TypeSet paramMap_TYPE) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, null, null,
|
||||
@@ -182,13 +118,19 @@ import java.util.List;
|
||||
paramMap_TYPE, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||
List<String> propValueList, ContentValues paramMap) {
|
||||
return addExpectedNodeWithOrder(propName, propValue, propValueList, null, paramMap,
|
||||
null, null);
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||
List<String> propValueList, byte[] propValue_bytes,
|
||||
ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
|
||||
if (propValue == null && propValueList != null) {
|
||||
propValue = concatinateListWithSemiColon(propValueList);
|
||||
}
|
||||
PropertyNode propertyNode = new PropertyNode(propName,
|
||||
final PropertyNode propertyNode = new PropertyNode(propName,
|
||||
propValue, propValueList, propValue_bytes,
|
||||
paramMap, paramMap_TYPE, propGroupSet);
|
||||
List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
|
||||
@@ -267,8 +209,9 @@ import java.util.List;
|
||||
for (PropertyNode actualNode : vnode.propList) {
|
||||
verifyNode(actualNode.propName, actualNode);
|
||||
}
|
||||
|
||||
if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) {
|
||||
List<String> expectedProps = new ArrayList<String>();
|
||||
final List<String> expectedProps = new ArrayList<String>();
|
||||
for (List<PropertyNode> nodes : mOrderedNodeMap.values()) {
|
||||
for (PropertyNode node : nodes) {
|
||||
if (!expectedProps.contains(node.propName)) {
|
||||
@@ -281,18 +224,19 @@ import java.util.List;
|
||||
expectedProps.add(node.propName);
|
||||
}
|
||||
}
|
||||
mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
|
||||
TestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
|
||||
+ " was not found.");
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyNode(final String propName, final PropertyNode actualNode) {
|
||||
List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
|
||||
final List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
|
||||
final int size = (expectedNodeList != null ? expectedNodeList.size() : 0);
|
||||
if (size > 0) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
PropertyNode expectedNode = expectedNodeList.get(i);
|
||||
List<PropertyNode> expectedButDifferentValueList = new ArrayList<PropertyNode>();
|
||||
final PropertyNode expectedNode = expectedNodeList.get(i);
|
||||
final List<PropertyNode> expectedButDifferentValueList =
|
||||
new ArrayList<PropertyNode>();
|
||||
if (expectedNode.propName.equals(propName)) {
|
||||
if (expectedNode.equals(actualNode)) {
|
||||
expectedNodeList.remove(i);
|
||||
@@ -318,7 +262,7 @@ import java.util.List;
|
||||
expectedButDifferentValueList);
|
||||
} else {
|
||||
// There's no expected node with same propName.
|
||||
mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
||||
TestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -333,7 +277,7 @@ import java.util.List;
|
||||
expectedButDifferentValueList);
|
||||
} else {
|
||||
// There's no expected node with same propName.
|
||||
mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
||||
TestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,7 +323,7 @@ import java.util.List;
|
||||
builder.append(expectedNode.toString());
|
||||
builder.append("\n");
|
||||
}
|
||||
mTestCase.fail("Property \"" + propName + "\" has wrong value.\n"
|
||||
TestCase.fail("Property \"" + propName + "\" has wrong value.\n"
|
||||
+ builder.toString()
|
||||
+ " actual: " + actualNode.toString());
|
||||
}
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
@@ -24,8 +24,9 @@ import android.test.AndroidTestCase;
|
||||
* Please do not add each unit test here.
|
||||
*/
|
||||
public class VCardTestsBase extends AndroidTestCase {
|
||||
public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8;
|
||||
public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8;
|
||||
public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC;
|
||||
public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC;
|
||||
public static final int V40 = VCardConfig.VCARD_TYPE_V40_GENERIC;
|
||||
|
||||
// Do not modify these during tests.
|
||||
protected final ContentValues mContentValuesForQP;
|
||||
@@ -41,6 +42,7 @@ public class VCardTestsBase extends AndroidTestCase {
|
||||
|
||||
public VCardTestsBase() {
|
||||
super();
|
||||
// Not using constants in vCard code since it may be wrong.
|
||||
mContentValuesForQP = new ContentValues();
|
||||
mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
|
||||
mContentValuesForSJis = new ContentValues();
|
||||
@@ -13,9 +13,8 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.EntityIterator;
|
||||
@@ -26,50 +25,62 @@ import android.pim.vcard.VCardEntryConstructor;
|
||||
import android.pim.vcard.VCardInterpreter;
|
||||
import android.pim.vcard.VCardInterpreterCollection;
|
||||
import android.pim.vcard.VCardParser;
|
||||
import android.pim.vcard.VCardParser_V21;
|
||||
import android.pim.vcard.VCardParser_V30;
|
||||
import android.pim.vcard.VCardUtils;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.mock.MockContext;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
/* package */ class CustomMockContext extends MockContext {
|
||||
final ContentResolver mResolver;
|
||||
public CustomMockContext(ContentResolver resolver) {
|
||||
mResolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return mResolver;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ class VCardVerifier {
|
||||
/**
|
||||
* <p>
|
||||
* The class lets users checks that given expected vCard data are same as given actual vCard data.
|
||||
* Able to verify both vCard importer/exporter.
|
||||
* </p>
|
||||
* <p>
|
||||
* First a user has to initialize the object by calling either
|
||||
* {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}.
|
||||
* "Round trip test" (import -> export -> import, or export -> import -> export) is not supported.
|
||||
* </p>
|
||||
*/
|
||||
public class VCardVerifier {
|
||||
private static final String LOG_TAG = "VCardVerifier";
|
||||
|
||||
private static class CustomMockContext extends MockContext {
|
||||
final ContentResolver mResolver;
|
||||
public CustomMockContext(ContentResolver resolver) {
|
||||
mResolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return mResolver;
|
||||
}
|
||||
}
|
||||
|
||||
private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
|
||||
public boolean onInit(Context context) {
|
||||
return true;
|
||||
}
|
||||
public boolean onEntryCreated(String vcard) {
|
||||
verifyOneVCard(vcard);
|
||||
verifyOneVCardForExport(vcard);
|
||||
return true;
|
||||
}
|
||||
public void onTerminate() {
|
||||
}
|
||||
}
|
||||
|
||||
private final AndroidTestCase mTestCase;
|
||||
private final AndroidTestCase mAndroidTestCase;
|
||||
private final VCardVerifierInternal mVCardVerifierInternal;
|
||||
private int mVCardType;
|
||||
private boolean mIsV30;
|
||||
private boolean mIsDoCoMo;
|
||||
|
||||
// Only one of them must be non-empty.
|
||||
@@ -83,9 +94,11 @@ import java.util.Arrays;
|
||||
private ContentValuesVerifier mContentValuesVerifier;
|
||||
private boolean mInitialized;
|
||||
private boolean mVerified = false;
|
||||
private String mCharset;
|
||||
|
||||
// Called by VCardTestsBase
|
||||
public VCardVerifier(AndroidTestCase androidTestCase) {
|
||||
mTestCase = androidTestCase;
|
||||
mAndroidTestCase = androidTestCase;
|
||||
mVCardVerifierInternal = new VCardVerifierInternal();
|
||||
mExportTestResolver = null;
|
||||
mInputStream = null;
|
||||
@@ -93,76 +106,101 @@ import java.util.Arrays;
|
||||
mVerified = false;
|
||||
}
|
||||
|
||||
public void initForExportTest(int vcardType) {
|
||||
if (mInitialized) {
|
||||
mTestCase.fail("Already initialized");
|
||||
}
|
||||
mExportTestResolver = new ExportTestResolver(mTestCase);
|
||||
mVCardType = vcardType;
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
// Should be called at the beginning of each import test.
|
||||
public void initForImportTest(int vcardType, int resId) {
|
||||
if (mInitialized) {
|
||||
mTestCase.fail("Already initialized");
|
||||
AndroidTestCase.fail("Already initialized");
|
||||
}
|
||||
mVCardType = vcardType;
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
setInputResourceId(resId);
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
// Should be called at the beginning of each export test.
|
||||
public void initForExportTest(int vcardType) {
|
||||
initForExportTest(vcardType, "UTF-8");
|
||||
}
|
||||
|
||||
public void initForExportTest(int vcardType, String charset) {
|
||||
if (mInitialized) {
|
||||
AndroidTestCase.fail("Already initialized");
|
||||
}
|
||||
mExportTestResolver = new ExportTestResolver(mAndroidTestCase);
|
||||
mVCardType = vcardType;
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
mInitialized = true;
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = "UTF-8";
|
||||
} else {
|
||||
mCharset = charset;
|
||||
}
|
||||
}
|
||||
|
||||
private void setInputResourceId(int resId) {
|
||||
InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId);
|
||||
InputStream inputStream = mAndroidTestCase.getContext().getResources().openRawResource(resId);
|
||||
if (inputStream == null) {
|
||||
mTestCase.fail("Wrong resId: " + resId);
|
||||
AndroidTestCase.fail("Wrong resId: " + resId);
|
||||
}
|
||||
setInputStream(inputStream);
|
||||
}
|
||||
|
||||
private void setInputStream(InputStream inputStream) {
|
||||
if (mExportTestResolver != null) {
|
||||
mTestCase.fail("addInputEntry() is called.");
|
||||
AndroidTestCase.fail("addInputEntry() is called.");
|
||||
} else if (mInputStream != null) {
|
||||
mTestCase.fail("InputStream is already set");
|
||||
AndroidTestCase.fail("InputStream is already set");
|
||||
}
|
||||
mInputStream = inputStream;
|
||||
}
|
||||
|
||||
public ContactEntry addInputEntry() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
AndroidTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mInputStream != null) {
|
||||
mTestCase.fail("setInputStream is called");
|
||||
AndroidTestCase.fail("setInputStream is called");
|
||||
}
|
||||
return mExportTestResolver.addInputContactEntry();
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithoutVersion() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
AndroidTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mPropertyNodesVerifier == null) {
|
||||
mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase);
|
||||
mPropertyNodesVerifier = new PropertyNodesVerifier(mAndroidTestCase);
|
||||
}
|
||||
PropertyNodesVerifierElem elem =
|
||||
mPropertyNodesVerifier.addPropertyNodesVerifierElem();
|
||||
elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1"));
|
||||
return mPropertyNodesVerifier.addPropertyNodesVerifierElem();
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
|
||||
final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElemWithoutVersion();
|
||||
final String versionString;
|
||||
if (VCardConfig.isVersion21(mVCardType)) {
|
||||
versionString = "2.1";
|
||||
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||
versionString = "3.0";
|
||||
} else if (VCardConfig.isVersion40(mVCardType)) {
|
||||
versionString = "4.0";
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected vcard type during a unit test");
|
||||
}
|
||||
elem.addExpectedNodeWithOrder("VERSION", versionString);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
AndroidTestCase.fail("Not initialized");
|
||||
}
|
||||
PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
|
||||
if (mIsV30) {
|
||||
elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", "");
|
||||
final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
|
||||
if (VCardConfig.isVersion40(mVCardType)) {
|
||||
elem.addExpectedNodeWithOrder("FN", "");
|
||||
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||
elem.addExpectedNodeWithOrder("N", "");
|
||||
elem.addExpectedNodeWithOrder("FN", "");
|
||||
} else if (mIsDoCoMo) {
|
||||
elem.addExpectedNodeWithOrder("N", "");
|
||||
}
|
||||
@@ -171,64 +209,75 @@ import java.util.Arrays;
|
||||
|
||||
public LineVerifierElem addLineVerifierElem() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
AndroidTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mLineVerifier == null) {
|
||||
mLineVerifier = new LineVerifier(mTestCase, mVCardType);
|
||||
mLineVerifier = new LineVerifier(mAndroidTestCase, mVCardType);
|
||||
}
|
||||
return mLineVerifier.addLineVerifierElem();
|
||||
}
|
||||
|
||||
public ContentValuesVerifierElem addContentValuesVerifierElem() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized");
|
||||
AndroidTestCase.fail("Not initialized");
|
||||
}
|
||||
if (mContentValuesVerifier == null) {
|
||||
mContentValuesVerifier = new ContentValuesVerifier();
|
||||
}
|
||||
|
||||
return mContentValuesVerifier.addElem(mTestCase);
|
||||
return mContentValuesVerifier.addElem(mAndroidTestCase);
|
||||
}
|
||||
|
||||
private void verifyOneVCard(final String vcard) {
|
||||
// Log.d("@@@", vcard);
|
||||
final VCardInterpreter builder;
|
||||
/**
|
||||
* Sets up sub-verifiers correctly and try parse given vCard as InputStream.
|
||||
* Errors around InputStream must be handled outside this method.
|
||||
*
|
||||
* Used both from {@link #verifyForImportTest()} and from {@link #verifyForExportTest()}.
|
||||
*/
|
||||
private void verifyWithInputStream(InputStream is) throws IOException {
|
||||
final VCardInterpreter interpreter;
|
||||
if (mContentValuesVerifier != null) {
|
||||
final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier;
|
||||
final VCardEntryConstructor vcardDataBuilder =
|
||||
new VCardEntryConstructor(mVCardType);
|
||||
vcardDataBuilder.addEntryHandler(mContentValuesVerifier);
|
||||
final VCardEntryConstructor constructor = new VCardEntryConstructor(mVCardType);
|
||||
constructor.addEntryHandler(mContentValuesVerifier);
|
||||
if (mPropertyNodesVerifier != null) {
|
||||
builder = new VCardInterpreterCollection(Arrays.asList(
|
||||
mPropertyNodesVerifier, vcardDataBuilder));
|
||||
interpreter = new VCardInterpreterCollection(Arrays.asList(
|
||||
mPropertyNodesVerifier, constructor));
|
||||
} else {
|
||||
builder = vnodeBuilder;
|
||||
interpreter = constructor;
|
||||
}
|
||||
} else {
|
||||
if (mPropertyNodesVerifier != null) {
|
||||
builder = mPropertyNodesVerifier;
|
||||
interpreter = mPropertyNodesVerifier;
|
||||
} else {
|
||||
return;
|
||||
interpreter = null;
|
||||
}
|
||||
}
|
||||
|
||||
final VCardParser parser =
|
||||
(mIsV30 ? new VCardParser_V30(true) : new VCardParser_V21());
|
||||
try {
|
||||
// Note: we must not specify charset toward vCard parsers. This code checks whether
|
||||
// those parsers are able to encode given binary without any extra information for
|
||||
// charset.
|
||||
final VCardParser parser = VCardUtils.getAppropriateParser(mVCardType);
|
||||
parser.parse(is, interpreter);
|
||||
} catch (VCardException e) {
|
||||
AndroidTestCase.fail("Unexpected VCardException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyOneVCardForExport(final String vcard) {
|
||||
Log.d(LOG_TAG, vcard);
|
||||
InputStream is = null;
|
||||
try {
|
||||
String charset =
|
||||
(VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8");
|
||||
is = new ByteArrayInputStream(vcard.getBytes(charset));
|
||||
mTestCase.assertEquals(true, parser.parse(is, null, builder));
|
||||
is = new ByteArrayInputStream(vcard.getBytes(mCharset));
|
||||
verifyWithInputStream(is);
|
||||
} catch (IOException e) {
|
||||
mTestCase.fail("Unexpected IOException: " + e.getMessage());
|
||||
} catch (VCardException e) {
|
||||
mTestCase.fail("Unexpected VCardException: " + e.getMessage());
|
||||
AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,33 +285,41 @@ import java.util.Arrays;
|
||||
|
||||
public void verify() {
|
||||
if (!mInitialized) {
|
||||
mTestCase.fail("Not initialized.");
|
||||
TestCase.fail("Not initialized.");
|
||||
}
|
||||
if (mVerified) {
|
||||
mTestCase.fail("verify() was called twice.");
|
||||
TestCase.fail("verify() was called twice.");
|
||||
}
|
||||
|
||||
if (mInputStream != null) {
|
||||
try {
|
||||
verifyForImportTest();
|
||||
} catch (IOException e) {
|
||||
mTestCase.fail("IOException was thrown: " + e.getMessage());
|
||||
} catch (VCardException e) {
|
||||
mTestCase.fail("VCardException was thrown: " + e.getMessage());
|
||||
if (mExportTestResolver != null){
|
||||
TestCase.fail("There are two input sources.");
|
||||
}
|
||||
verifyForImportTest();
|
||||
} else if (mExportTestResolver != null){
|
||||
verifyForExportTest();
|
||||
} else {
|
||||
mTestCase.fail("No input is determined");
|
||||
TestCase.fail("No input is determined");
|
||||
}
|
||||
mVerified = true;
|
||||
}
|
||||
|
||||
private void verifyForImportTest() throws IOException, VCardException {
|
||||
private void verifyForImportTest() {
|
||||
if (mLineVerifier != null) {
|
||||
mTestCase.fail("Not supported now.");
|
||||
AndroidTestCase.fail("Not supported now.");
|
||||
}
|
||||
if (mContentValuesVerifier != null) {
|
||||
mContentValuesVerifier.verify(mInputStream, mVCardType);
|
||||
|
||||
try {
|
||||
verifyWithInputStream(mInputStream);
|
||||
} catch (IOException e) {
|
||||
AndroidTestCase.fail("IOException was thrown: " + e.getMessage());
|
||||
} finally {
|
||||
if (mInputStream != null) {
|
||||
try {
|
||||
mInputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +344,7 @@ import java.util.Arrays;
|
||||
|
||||
private void verifyForExportTest() {
|
||||
final VCardComposer composer =
|
||||
new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType);
|
||||
new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset);
|
||||
composer.addHandler(mLineVerifier);
|
||||
composer.addHandler(mVCardVerifierInternal);
|
||||
if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
|
||||
@@ -303,7 +360,7 @@ import java.util.Arrays;
|
||||
composer.createOneEntry(mockGetEntityIteratorMethod));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
AndroidTestCase.fail();
|
||||
AndroidTestCase.fail(e.toString());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,18 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
package android.pim.vcard.test_utils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardInterpreter;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.VCardInterpreter;
|
||||
import android.pim.vcard.VCardUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.net.QuotedPrintableCodec;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -32,34 +30,29 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Store the parse result to custom datastruct: VNode, PropertyNode
|
||||
* <p>
|
||||
* The class storing the parse result to custom datastruct:
|
||||
* {@link VNode}, and {@link PropertyNode}.
|
||||
* Maybe several vcard instance, so use vNodeList to store.
|
||||
* VNode: standy by a vcard instance.
|
||||
* PropertyNode: standy by a property line of a card.
|
||||
*
|
||||
* Previously used in main vCard handling code but now exists only for testing.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal).
|
||||
* </p>
|
||||
*/
|
||||
public class VNodeBuilder implements VCardInterpreter {
|
||||
static private String LOG_TAG = "VNodeBuilder";
|
||||
|
||||
/**
|
||||
* If there's no other information available, this class uses this charset for encoding
|
||||
* byte arrays.
|
||||
*/
|
||||
static public String TARGET_CHARSET = "UTF-8";
|
||||
|
||||
/** type=VNode */
|
||||
public List<VNode> vNodeList = new ArrayList<VNode>();
|
||||
private int mNodeListPos = 0;
|
||||
private VNode mCurrentVNode;
|
||||
private PropertyNode mCurrentPropNode;
|
||||
private String mCurrentParamType;
|
||||
|
||||
|
||||
/**
|
||||
* The charset using which VParser parses the text.
|
||||
*/
|
||||
private String mSourceCharset;
|
||||
|
||||
|
||||
/**
|
||||
* The charset with which byte array is encoded to String.
|
||||
*/
|
||||
@@ -68,27 +61,15 @@ public class VNodeBuilder implements VCardInterpreter {
|
||||
private boolean mStrictLineBreakParsing;
|
||||
|
||||
public VNodeBuilder() {
|
||||
this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false);
|
||||
this(VCardConfig.DEFAULT_IMPORT_CHARSET, false);
|
||||
}
|
||||
|
||||
public VNodeBuilder(String charset, boolean strictLineBreakParsing) {
|
||||
this(null, charset, strictLineBreakParsing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide sourceCharset is temporal.
|
||||
*/
|
||||
public VNodeBuilder(String sourceCharset, String targetCharset,
|
||||
boolean strictLineBreakParsing) {
|
||||
if (sourceCharset != null) {
|
||||
mSourceCharset = sourceCharset;
|
||||
} else {
|
||||
mSourceCharset = VCardConfig.DEFAULT_CHARSET;
|
||||
}
|
||||
public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) {
|
||||
mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
|
||||
if (targetCharset != null) {
|
||||
mTargetCharset = targetCharset;
|
||||
} else {
|
||||
mTargetCharset = TARGET_CHARSET;
|
||||
mTargetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
|
||||
}
|
||||
mStrictLineBreakParsing = strictLineBreakParsing;
|
||||
}
|
||||
@@ -149,7 +130,6 @@ public class VNodeBuilder implements VCardInterpreter {
|
||||
mCurrentPropNode.propName = name;
|
||||
}
|
||||
|
||||
// Used only in VCard.
|
||||
public void propertyGroup(String group) {
|
||||
mCurrentPropNode.propGroupSet.add(group);
|
||||
}
|
||||
@@ -159,6 +139,12 @@ public class VNodeBuilder implements VCardInterpreter {
|
||||
}
|
||||
|
||||
public void propertyParamValue(String value) {
|
||||
if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
|
||||
value = VCardUtils.convertStringCharset(value,
|
||||
VCardConfig.DEFAULT_INTERMEDIATE_CHARSET,
|
||||
VCardConfig.DEFAULT_IMPORT_CHARSET);
|
||||
}
|
||||
|
||||
if (mCurrentParamType == null ||
|
||||
mCurrentParamType.equalsIgnoreCase("TYPE")) {
|
||||
mCurrentPropNode.paramMap_TYPE.add(value);
|
||||
@@ -192,71 +178,11 @@ public class VNodeBuilder implements VCardInterpreter {
|
||||
encoding = encoding.toUpperCase();
|
||||
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
||||
// Assume BASE64 is used only when the number of values is 1.
|
||||
mCurrentPropNode.propValue_bytes =
|
||||
Base64.decodeBase64(value.getBytes());
|
||||
mCurrentPropNode.propValue_bytes = Base64.decode(value.getBytes(), Base64.NO_WRAP);
|
||||
return value;
|
||||
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
||||
String quotedPrintable = value
|
||||
.replaceAll("= ", " ").replaceAll("=\t", "\t");
|
||||
String[] lines;
|
||||
if (mStrictLineBreakParsing) {
|
||||
lines = quotedPrintable.split("\r\n");
|
||||
} else {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = quotedPrintable.length();
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = quotedPrintable.charAt(i);
|
||||
if (ch == '\n') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
} else if (ch == '\r') {
|
||||
list.add(builder.toString());
|
||||
builder = new StringBuilder();
|
||||
if (i < length - 1) {
|
||||
char nextCh = quotedPrintable.charAt(i + 1);
|
||||
if (nextCh == '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
String finalLine = builder.toString();
|
||||
if (finalLine.length() > 0) {
|
||||
list.add(finalLine);
|
||||
}
|
||||
lines = list.toArray(new String[0]);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
if (line.endsWith("=")) {
|
||||
line = line.substring(0, line.length() - 1);
|
||||
}
|
||||
builder.append(line);
|
||||
}
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = builder.toString().getBytes(mSourceCharset);
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
|
||||
bytes = builder.toString().getBytes();
|
||||
}
|
||||
|
||||
try {
|
||||
bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
|
||||
} catch (DecoderException e) {
|
||||
Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return new String(bytes, targetCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
|
||||
return new String(bytes);
|
||||
}
|
||||
return VCardUtils.parseQuotedPrintable(
|
||||
value, mStrictLineBreakParsing, mSourceCharset, targetCharset);
|
||||
}
|
||||
// Unknown encoding. Fall back to default.
|
||||
}
|
||||
@@ -309,6 +235,6 @@ public class VNodeBuilder implements VCardInterpreter {
|
||||
}
|
||||
|
||||
public String getResult(){
|
||||
return null;
|
||||
throw new RuntimeException("Not supported");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user