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>();
|
new HashMap<Character, String>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// There's no logical mapping rule in Unicode. Sigh.
|
|
||||||
sHalfWidthMap.put('\u3001', "\uFF64");
|
sHalfWidthMap.put('\u3001', "\uFF64");
|
||||||
sHalfWidthMap.put('\u3002', "\uFF61");
|
sHalfWidthMap.put('\u3002', "\uFF61");
|
||||||
sHalfWidthMap.put('\u300C', "\uFF62");
|
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
|
* @param ch input character
|
||||||
* @return CharSequence object if the mapping for ch exists. Return null otherwise.
|
* @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)) {
|
if (sHalfWidthMap.containsKey(ch)) {
|
||||||
return sHalfWidthMap.get(ch);
|
return sHalfWidthMap.get(ch);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,11 +30,10 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
|||||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||||
import android.telephony.PhoneNumberUtils;
|
import android.telephony.PhoneNumberUtils;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
import android.util.CharsetUtils;
|
import android.util.CharsetUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -47,7 +46,23 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
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 {
|
public class VCardBuilder {
|
||||||
private static final String LOG_TAG = "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_WS = " ";
|
||||||
private static final String VCARD_PARAM_EQUAL = "=";
|
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_QP =
|
||||||
|
"ENCODING=" + VCardConstants.PARAM_ENCODING_QP;
|
||||||
private static final String VCARD_PARAM_ENCODING_BASE64_V21 = "ENCODING=BASE64";
|
private static final String VCARD_PARAM_ENCODING_BASE64_V21 =
|
||||||
private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b";
|
"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 SHIFT_JIS = "SHIFT_JIS";
|
||||||
private static final String UTF_8 = "UTF-8";
|
|
||||||
|
|
||||||
private final int mVCardType;
|
private final int mVCardType;
|
||||||
|
|
||||||
private final boolean mIsV30;
|
private final boolean mIsV30OrV40;
|
||||||
private final boolean mIsJapaneseMobilePhone;
|
private final boolean mIsJapaneseMobilePhone;
|
||||||
private final boolean mOnlyOneNoteFieldIsAvailable;
|
private final boolean mOnlyOneNoteFieldIsAvailable;
|
||||||
private final boolean mIsDoCoMo;
|
private final boolean mIsDoCoMo;
|
||||||
private final boolean mShouldUseQuotedPrintable;
|
private final boolean mShouldUseQuotedPrintable;
|
||||||
private final boolean mUsesAndroidProperty;
|
private final boolean mUsesAndroidProperty;
|
||||||
private final boolean mUsesDefactProperty;
|
private final boolean mUsesDefactProperty;
|
||||||
private final boolean mUsesUtf8;
|
|
||||||
private final boolean mUsesShiftJis;
|
|
||||||
private final boolean mAppendTypeParamName;
|
private final boolean mAppendTypeParamName;
|
||||||
private final boolean mRefrainsQPToNameProperties;
|
private final boolean mRefrainsQPToNameProperties;
|
||||||
private final boolean mNeedsToConvertPhoneticString;
|
private final boolean mNeedsToConvertPhoneticString;
|
||||||
|
|
||||||
private final boolean mShouldAppendCharsetParam;
|
private final boolean mShouldAppendCharsetParam;
|
||||||
|
|
||||||
private final String mCharsetString;
|
private final String mCharset;
|
||||||
private final String mVCardCharsetParameter;
|
private final String mVCardCharsetParameter;
|
||||||
|
|
||||||
private StringBuilder mBuilder;
|
private StringBuilder mBuilder;
|
||||||
private boolean mEndAppended;
|
private boolean mEndAppended;
|
||||||
|
|
||||||
public VCardBuilder(final int vcardType) {
|
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;
|
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);
|
mShouldUseQuotedPrintable = VCardConfig.shouldUseQuotedPrintable(vcardType);
|
||||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||||
mIsJapaneseMobilePhone = VCardConfig.needsToConvertPhoneticString(vcardType);
|
mIsJapaneseMobilePhone = VCardConfig.needsToConvertPhoneticString(vcardType);
|
||||||
mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType);
|
mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType);
|
||||||
mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType);
|
mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType);
|
||||||
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
|
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
|
||||||
mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
|
|
||||||
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
|
|
||||||
mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType);
|
mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType);
|
||||||
mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
|
mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
|
||||||
mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(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) {
|
if (VCardConfig.isDoCoMo(vcardType)) {
|
||||||
String charset;
|
if (!SHIFT_JIS.equalsIgnoreCase(charset)) {
|
||||||
try {
|
Log.w(LOG_TAG,
|
||||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
"The charset \"" + charset + "\" is used while "
|
||||||
} catch (UnsupportedCharsetException e) {
|
+ SHIFT_JIS + " is needed to be used.");
|
||||||
Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
if (TextUtils.isEmpty(charset)) {
|
||||||
charset = SHIFT_JIS;
|
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;
|
mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
|
||||||
} else {
|
} else {
|
||||||
mCharsetString = UTF_8;
|
if (TextUtils.isEmpty(charset)) {
|
||||||
mVCardCharsetParameter = "CHARSET=" + UTF_8;
|
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();
|
clear();
|
||||||
}
|
}
|
||||||
@@ -158,9 +221,14 @@ public class VCardBuilder {
|
|||||||
mBuilder = new StringBuilder();
|
mBuilder = new StringBuilder();
|
||||||
mEndAppended = false;
|
mEndAppended = false;
|
||||||
appendLine(VCardConstants.PROPERTY_BEGIN, VCARD_DATA_VCARD);
|
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);
|
appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V30);
|
||||||
} else {
|
} else {
|
||||||
|
if (!VCardConfig.isVersion21(mVCardType)) {
|
||||||
|
Log.w(LOG_TAG, "Unknown vCard version detected.");
|
||||||
|
}
|
||||||
appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V21);
|
appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V21);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,19 +294,128 @@ public class VCardBuilder {
|
|||||||
return primaryContentValues;
|
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
|
* 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
|
* may get confused with multiple "N", "FN", etc. properties, though it is valid in
|
||||||
* vCard spec.
|
* vCard spec.
|
||||||
*/
|
*/
|
||||||
public VCardBuilder appendNameProperties(final List<ContentValues> contentValuesList) {
|
public VCardBuilder appendNameProperties(final List<ContentValues> contentValuesList) {
|
||||||
|
if (VCardConfig.isVersion40(mVCardType)) {
|
||||||
|
return appendNamePropertiesV40(contentValuesList);
|
||||||
|
}
|
||||||
|
|
||||||
if (contentValuesList == null || contentValuesList.isEmpty()) {
|
if (contentValuesList == null || contentValuesList.isEmpty()) {
|
||||||
if (mIsDoCoMo) {
|
if (VCardConfig.isVersion30(mVCardType)) {
|
||||||
appendLine(VCardConstants.PROPERTY_N, "");
|
|
||||||
} else if (mIsV30) {
|
|
||||||
// vCard 3.0 requires "N" and "FN" properties.
|
// 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_N, "");
|
||||||
appendLine(VCardConstants.PROPERTY_FN, "");
|
appendLine(VCardConstants.PROPERTY_FN, "");
|
||||||
|
} else if (mIsDoCoMo) {
|
||||||
|
appendLine(VCardConstants.PROPERTY_N, "");
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -360,6 +537,7 @@ public class VCardBuilder {
|
|||||||
encodeQuotedPrintable(displayName) :
|
encodeQuotedPrintable(displayName) :
|
||||||
escapeCharacters(displayName);
|
escapeCharacters(displayName);
|
||||||
|
|
||||||
|
// N
|
||||||
mBuilder.append(VCardConstants.PROPERTY_N);
|
mBuilder.append(VCardConstants.PROPERTY_N);
|
||||||
if (shouldAppendCharsetParam(displayName)) {
|
if (shouldAppendCharsetParam(displayName)) {
|
||||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
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_ITEM_SEPARATOR);
|
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||||
mBuilder.append(VCARD_END_OF_LINE);
|
mBuilder.append(VCARD_END_OF_LINE);
|
||||||
|
|
||||||
|
// FN
|
||||||
mBuilder.append(VCardConstants.PROPERTY_FN);
|
mBuilder.append(VCardConstants.PROPERTY_FN);
|
||||||
|
|
||||||
// Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it
|
// 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
|
// when it would be useful or necessary for external importers,
|
||||||
// importer allows this vioration.
|
// assuming the external importer allows this vioration of the spec.
|
||||||
if (shouldAppendCharsetParam(displayName)) {
|
if (shouldAppendCharsetParam(displayName)) {
|
||||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||||
mBuilder.append(mVCardCharsetParameter);
|
mBuilder.append(mVCardCharsetParameter);
|
||||||
@@ -388,8 +568,7 @@ public class VCardBuilder {
|
|||||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||||
mBuilder.append(encodedDisplayName);
|
mBuilder.append(encodedDisplayName);
|
||||||
mBuilder.append(VCARD_END_OF_LINE);
|
mBuilder.append(VCARD_END_OF_LINE);
|
||||||
} else if (mIsV30) {
|
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||||
// vCard 3.0 specification requires these fields.
|
|
||||||
appendLine(VCardConstants.PROPERTY_N, "");
|
appendLine(VCardConstants.PROPERTY_N, "");
|
||||||
appendLine(VCardConstants.PROPERTY_FN, "");
|
appendLine(VCardConstants.PROPERTY_FN, "");
|
||||||
} else if (mIsDoCoMo) {
|
} else if (mIsDoCoMo) {
|
||||||
@@ -400,6 +579,9 @@ public class VCardBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits SOUND;IRMC, SORT-STRING, and de-fact values for phonetic names like X-PHONETIC-FAMILY.
|
||||||
|
*/
|
||||||
private void appendPhoneticNameFields(final ContentValues contentValues) {
|
private void appendPhoneticNameFields(final ContentValues contentValues) {
|
||||||
final String phoneticFamilyName;
|
final String phoneticFamilyName;
|
||||||
final String phoneticMiddleName;
|
final String phoneticMiddleName;
|
||||||
@@ -439,13 +621,18 @@ public class VCardBuilder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to emit the field(s) related to phonetic name.
|
if (VCardConfig.isVersion40(mVCardType)) {
|
||||||
if (mIsV30) {
|
// We don't want SORT-STRING anyway.
|
||||||
final String sortString = VCardUtils
|
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||||
.constructNameFromElements(mVCardType,
|
final String sortString =
|
||||||
|
VCardUtils.constructNameFromElements(mVCardType,
|
||||||
phoneticFamilyName, phoneticMiddleName, phoneticGivenName);
|
phoneticFamilyName, phoneticMiddleName, phoneticGivenName);
|
||||||
mBuilder.append(VCardConstants.PROPERTY_SORT_STRING);
|
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(VCARD_PARAM_SEPARATOR);
|
||||||
mBuilder.append(mVCardCharsetParameter);
|
mBuilder.append(mVCardCharsetParameter);
|
||||||
}
|
}
|
||||||
@@ -454,18 +641,18 @@ public class VCardBuilder {
|
|||||||
mBuilder.append(VCARD_END_OF_LINE);
|
mBuilder.append(VCARD_END_OF_LINE);
|
||||||
} else if (mIsJapaneseMobilePhone) {
|
} else if (mIsJapaneseMobilePhone) {
|
||||||
// Note: There is no appropriate property for expressing
|
// 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).
|
// vCard 3.0 (SORT-STRING).
|
||||||
// We chose to use DoCoMo's way when the device is Japanese one
|
// We use DoCoMo's way when the device is Japanese one since it is already
|
||||||
// since it is supported by
|
// supported by a lot of Japanese mobile phones.
|
||||||
// a lot of Japanese mobile phones. This is "X-" property, so
|
// This is "X-" property, so any parser hopefully would not get
|
||||||
// any parser hopefully would not get confused with this.
|
// confused with this.
|
||||||
//
|
//
|
||||||
// Also, DoCoMo's specification requires vCard composer to use just the first
|
// Also, DoCoMo's specification requires vCard composer to use just the first
|
||||||
// column.
|
// column.
|
||||||
// i.e.
|
// i.e.
|
||||||
// o SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
|
// good: SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
|
||||||
// x SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
|
// bad : SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
|
||||||
mBuilder.append(VCardConstants.PROPERTY_SOUND);
|
mBuilder.append(VCardConstants.PROPERTY_SOUND);
|
||||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||||
mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N);
|
mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N);
|
||||||
@@ -519,13 +706,14 @@ public class VCardBuilder {
|
|||||||
mBuilder.append(encodedPhoneticGivenName);
|
mBuilder.append(encodedPhoneticGivenName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
mBuilder.append(VCARD_ITEM_SEPARATOR); // family;given
|
||||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
mBuilder.append(VCARD_ITEM_SEPARATOR); // given;middle
|
||||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
mBuilder.append(VCARD_ITEM_SEPARATOR); // middle;prefix
|
||||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
mBuilder.append(VCARD_ITEM_SEPARATOR); // prefix;suffix
|
||||||
mBuilder.append(VCARD_END_OF_LINE);
|
mBuilder.append(VCARD_END_OF_LINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d("@@@", "hoge");
|
||||||
if (mUsesDefactProperty) {
|
if (mUsesDefactProperty) {
|
||||||
if (!TextUtils.isEmpty(phoneticGivenName)) {
|
if (!TextUtils.isEmpty(phoneticGivenName)) {
|
||||||
final boolean reallyUseQuotedPrintable =
|
final boolean reallyUseQuotedPrintable =
|
||||||
@@ -549,7 +737,7 @@ public class VCardBuilder {
|
|||||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||||
mBuilder.append(encodedPhoneticGivenName);
|
mBuilder.append(encodedPhoneticGivenName);
|
||||||
mBuilder.append(VCARD_END_OF_LINE);
|
mBuilder.append(VCARD_END_OF_LINE);
|
||||||
}
|
} // if (!TextUtils.isEmpty(phoneticGivenName))
|
||||||
if (!TextUtils.isEmpty(phoneticMiddleName)) {
|
if (!TextUtils.isEmpty(phoneticMiddleName)) {
|
||||||
final boolean reallyUseQuotedPrintable =
|
final boolean reallyUseQuotedPrintable =
|
||||||
(mShouldUseQuotedPrintable &&
|
(mShouldUseQuotedPrintable &&
|
||||||
@@ -572,7 +760,7 @@ public class VCardBuilder {
|
|||||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||||
mBuilder.append(encodedPhoneticMiddleName);
|
mBuilder.append(encodedPhoneticMiddleName);
|
||||||
mBuilder.append(VCARD_END_OF_LINE);
|
mBuilder.append(VCARD_END_OF_LINE);
|
||||||
}
|
} // if (!TextUtils.isEmpty(phoneticGivenName))
|
||||||
if (!TextUtils.isEmpty(phoneticFamilyName)) {
|
if (!TextUtils.isEmpty(phoneticFamilyName)) {
|
||||||
final boolean reallyUseQuotedPrintable =
|
final boolean reallyUseQuotedPrintable =
|
||||||
(mShouldUseQuotedPrintable &&
|
(mShouldUseQuotedPrintable &&
|
||||||
@@ -595,13 +783,13 @@ public class VCardBuilder {
|
|||||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||||
mBuilder.append(encodedPhoneticFamilyName);
|
mBuilder.append(encodedPhoneticFamilyName);
|
||||||
mBuilder.append(VCARD_END_OF_LINE);
|
mBuilder.append(VCARD_END_OF_LINE);
|
||||||
}
|
} // if (!TextUtils.isEmpty(phoneticFamilyName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardBuilder appendNickNames(final List<ContentValues> contentValuesList) {
|
public VCardBuilder appendNickNames(final List<ContentValues> contentValuesList) {
|
||||||
final boolean useAndroidProperty;
|
final boolean useAndroidProperty;
|
||||||
if (mIsV30) {
|
if (mIsV30OrV40) { // These specifications have NICKNAME property.
|
||||||
useAndroidProperty = false;
|
useAndroidProperty = false;
|
||||||
} else if (mUsesAndroidProperty) {
|
} else if (mUsesAndroidProperty) {
|
||||||
useAndroidProperty = true;
|
useAndroidProperty = true;
|
||||||
@@ -922,21 +1110,21 @@ public class VCardBuilder {
|
|||||||
encodedCountry = escapeCharacters(rawCountry);
|
encodedCountry = escapeCharacters(rawCountry);
|
||||||
encodedNeighborhood = escapeCharacters(rawNeighborhood);
|
encodedNeighborhood = escapeCharacters(rawNeighborhood);
|
||||||
}
|
}
|
||||||
final StringBuffer addressBuffer = new StringBuffer();
|
final StringBuilder addressBuilder = new StringBuilder();
|
||||||
addressBuffer.append(encodedPoBox);
|
addressBuilder.append(encodedPoBox);
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street
|
||||||
addressBuffer.append(encodedStreet);
|
addressBuilder.append(encodedStreet);
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality
|
||||||
addressBuffer.append(encodedLocality);
|
addressBuilder.append(encodedLocality);
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region
|
||||||
addressBuffer.append(encodedRegion);
|
addressBuilder.append(encodedRegion);
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code
|
||||||
addressBuffer.append(encodedPostalCode);
|
addressBuilder.append(encodedPostalCode);
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country
|
||||||
addressBuffer.append(encodedCountry);
|
addressBuilder.append(encodedCountry);
|
||||||
return new PostalStruct(
|
return new PostalStruct(
|
||||||
reallyUseQuotedPrintable, appendCharset, addressBuffer.toString());
|
reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
|
||||||
} else { // VCardUtils.areAllEmpty(rawAddressArray) == true
|
} else { // VCardUtils.areAllEmpty(rawAddressArray) == true
|
||||||
// Try to use FORMATTED_ADDRESS instead.
|
// Try to use FORMATTED_ADDRESS instead.
|
||||||
final String rawFormattedAddress =
|
final String rawFormattedAddress =
|
||||||
@@ -959,16 +1147,16 @@ public class VCardBuilder {
|
|||||||
// We use the second value ("Extended Address") just because Japanese mobile phones
|
// 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
|
// do so. If the other importer expects the value be in the other field, some flag may
|
||||||
// be needed.
|
// be needed.
|
||||||
final StringBuffer addressBuffer = new StringBuffer();
|
final StringBuilder addressBuilder = new StringBuilder();
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address
|
||||||
addressBuffer.append(encodedFormattedAddress);
|
addressBuilder.append(encodedFormattedAddress);
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code
|
||||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country
|
||||||
return new PostalStruct(
|
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.");
|
Log.d(LOG_TAG, "Unknown photo type. Ignored.");
|
||||||
continue;
|
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)) {
|
if (!TextUtils.isEmpty(photoString)) {
|
||||||
appendPhotoLine(photoString, photoType);
|
appendPhotoLine(photoString, photoType);
|
||||||
}
|
}
|
||||||
@@ -1165,6 +1354,8 @@ public class VCardBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public VCardBuilder appendEvents(final List<ContentValues> contentValuesList) {
|
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) {
|
if (contentValuesList != null) {
|
||||||
String primaryBirthday = null;
|
String primaryBirthday = null;
|
||||||
String secondaryBirthday = null;
|
String secondaryBirthday = null;
|
||||||
@@ -1232,16 +1423,19 @@ public class VCardBuilder {
|
|||||||
return this;
|
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,
|
public void appendPostalLine(final int type, final String label,
|
||||||
final ContentValues contentValues,
|
final ContentValues contentValues,
|
||||||
final boolean isPrimary, final boolean emitLineEveryTime) {
|
final boolean isPrimary, final boolean emitEveryTime) {
|
||||||
final boolean reallyUseQuotedPrintable;
|
final boolean reallyUseQuotedPrintable;
|
||||||
final boolean appendCharset;
|
final boolean appendCharset;
|
||||||
final String addressValue;
|
final String addressValue;
|
||||||
{
|
{
|
||||||
PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
|
PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
|
||||||
if (postalStruct == null) {
|
if (postalStruct == null) {
|
||||||
if (emitLineEveryTime) {
|
if (emitEveryTime) {
|
||||||
reallyUseQuotedPrintable = false;
|
reallyUseQuotedPrintable = false;
|
||||||
appendCharset = false;
|
appendCharset = false;
|
||||||
addressValue = "";
|
addressValue = "";
|
||||||
@@ -1463,7 +1657,7 @@ public class VCardBuilder {
|
|||||||
parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
|
parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
|
||||||
} else if (VCardUtils.isMobilePhoneLabel(label)) {
|
} else if (VCardUtils.isMobilePhoneLabel(label)) {
|
||||||
parameterList.add(VCardConstants.PARAM_TYPE_CELL);
|
parameterList.add(VCardConstants.PARAM_TYPE_CELL);
|
||||||
} else if (mIsV30) {
|
} else if (mIsV30OrV40) {
|
||||||
// This label is appropriately encoded in appendTypeParameters.
|
// This label is appropriately encoded in appendTypeParameters.
|
||||||
parameterList.add(label);
|
parameterList.add(label);
|
||||||
} else {
|
} else {
|
||||||
@@ -1526,8 +1720,8 @@ public class VCardBuilder {
|
|||||||
StringBuilder tmpBuilder = new StringBuilder();
|
StringBuilder tmpBuilder = new StringBuilder();
|
||||||
tmpBuilder.append(VCardConstants.PROPERTY_PHOTO);
|
tmpBuilder.append(VCardConstants.PROPERTY_PHOTO);
|
||||||
tmpBuilder.append(VCARD_PARAM_SEPARATOR);
|
tmpBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||||
if (mIsV30) {
|
if (mIsV30OrV40) {
|
||||||
tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V30);
|
tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_AS_B);
|
||||||
} else {
|
} else {
|
||||||
tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V21);
|
tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V21);
|
||||||
}
|
}
|
||||||
@@ -1559,7 +1753,8 @@ public class VCardBuilder {
|
|||||||
mBuilder.append(VCARD_END_OF_LINE);
|
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)) {
|
if (!sAllowedAndroidPropertySet.contains(mimeType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1681,7 +1876,7 @@ public class VCardBuilder {
|
|||||||
encodedValue = encodeQuotedPrintable(rawValue);
|
encodedValue = encodeQuotedPrintable(rawValue);
|
||||||
} else {
|
} else {
|
||||||
// TODO: one line may be too huge, which may be invalid in vCard spec, though
|
// 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);
|
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.
|
// which would be recommended way in vcard 3.0 though not valid in vCard 2.1.
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (final String typeValue : types) {
|
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
|
// 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
|
// 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.
|
// high probabilyty in which external importers cannot understand them.
|
||||||
@@ -1756,7 +1956,7 @@ public class VCardBuilder {
|
|||||||
} else {
|
} else {
|
||||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||||
}
|
}
|
||||||
appendTypeParameter(VCardUtils.toStringAvailableAsV30ParameValue(typeValue));
|
appendTypeParameter(encoded);
|
||||||
} else { // vCard 2.1
|
} else { // vCard 2.1
|
||||||
if (!VCardUtils.isV21Word(typeValue)) {
|
if (!VCardUtils.isV21Word(typeValue)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1783,7 +1983,8 @@ public class VCardBuilder {
|
|||||||
// device is DoCoMo's (just for safety).
|
// device is DoCoMo's (just for safety).
|
||||||
//
|
//
|
||||||
// Note: In vCard 3.0, Type strings also can be like this: "TYPE=HOME,PREF"
|
// 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(VCardConstants.PARAM_TYPE).append(VCARD_PARAM_EQUAL);
|
||||||
}
|
}
|
||||||
builder.append(type);
|
builder.append(type);
|
||||||
@@ -1825,9 +2026,9 @@ public class VCardBuilder {
|
|||||||
byte[] strArray = null;
|
byte[] strArray = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
strArray = str.getBytes(mCharsetString);
|
strArray = str.getBytes(mCharset);
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
Log.e(LOG_TAG, "Charset " + mCharsetString + " cannot be used. "
|
Log.e(LOG_TAG, "Charset " + mCharset + " cannot be used. "
|
||||||
+ "Try default charset");
|
+ "Try default charset");
|
||||||
strArray = str.getBytes();
|
strArray = str.getBytes();
|
||||||
}
|
}
|
||||||
@@ -1893,7 +2094,7 @@ public class VCardBuilder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '\\': {
|
case '\\': {
|
||||||
if (mIsV30) {
|
if (mIsV30OrV40) {
|
||||||
tmpBuilder.append("\\\\");
|
tmpBuilder.append("\\\\");
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@@ -1911,7 +2112,7 @@ public class VCardBuilder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ',': {
|
case ',': {
|
||||||
if (mIsV30) {
|
if (mIsV30OrV40) {
|
||||||
tmpBuilder.append("\\,");
|
tmpBuilder.append("\\,");
|
||||||
} else {
|
} else {
|
||||||
tmpBuilder.append(ch);
|
tmpBuilder.append(ch);
|
||||||
|
|||||||
@@ -19,16 +19,12 @@ import android.content.ContentResolver;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Entity;
|
import android.content.Entity;
|
||||||
import android.content.EntityIterator;
|
|
||||||
import android.content.Entity.NamedContentValues;
|
import android.content.Entity.NamedContentValues;
|
||||||
|
import android.content.EntityIterator;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.pim.vcard.exception.VCardException;
|
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.Email;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
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.StructuredName;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
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.CharsetUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -61,15 +62,11 @@ import java.util.Map;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* The class for composing VCard from Contacts information. Note that this is
|
* The class for composing vCard from Contacts information.
|
||||||
* completely differnt implementation from
|
|
||||||
* android.syncml.pim.vcard.VCardComposer, which is not maintained anymore.
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Usually, this class should be used like this.
|
* Usually, this class should be used like this.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
|
||||||
* <pre class="prettyprint">VCardComposer composer = null;
|
* <pre class="prettyprint">VCardComposer composer = null;
|
||||||
* try {
|
* try {
|
||||||
* composer = new VCardComposer(context);
|
* composer = new VCardComposer(context);
|
||||||
@@ -93,15 +90,18 @@ import java.util.Map;
|
|||||||
* if (composer != null) {
|
* if (composer != null) {
|
||||||
* composer.terminate();
|
* 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 {
|
public class VCardComposer {
|
||||||
private static final String LOG_TAG = "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 =
|
public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
|
||||||
"Failed to get database information";
|
"Failed to get database information";
|
||||||
|
|
||||||
@@ -119,6 +119,8 @@ public class VCardComposer {
|
|||||||
|
|
||||||
public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
|
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 SHIFT_JIS = "SHIFT_JIS";
|
||||||
private static final String UTF_8 = "UTF-8";
|
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_ICQ, VCardConstants.PROPERTY_X_ICQ);
|
||||||
sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
|
sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
|
||||||
sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
|
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 {
|
public static interface OneEntryHandler {
|
||||||
@@ -152,37 +154,37 @@ public class VCardComposer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <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>
|
||||||
* <p>
|
* <p>
|
||||||
* The input OutputStream object is closed() on {@link #onTerminate()}.
|
* The input OutputStream object is closed() on {@link #onTerminate()}.
|
||||||
* Must not close the stream outside.
|
* Must not close the stream outside this class.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class HandlerForOutputStream implements OneEntryHandler {
|
public final class HandlerForOutputStream implements OneEntryHandler {
|
||||||
@SuppressWarnings("hiding")
|
@SuppressWarnings("hiding")
|
||||||
private static final String LOG_TAG = "vcard.VCardComposer.HandlerForOutputStream";
|
private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream";
|
||||||
|
|
||||||
final private OutputStream mOutputStream; // mWriter will close this.
|
|
||||||
private Writer mWriter;
|
|
||||||
|
|
||||||
private boolean mOnTerminateIsCalled = false;
|
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.
|
* Input stream will be closed on the detruction of this object.
|
||||||
*/
|
*/
|
||||||
public HandlerForOutputStream(OutputStream outputStream) {
|
public HandlerForOutputStream(final OutputStream outputStream) {
|
||||||
mOutputStream = outputStream;
|
mOutputStream = outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInit(Context context) {
|
public boolean onInit(final Context context) {
|
||||||
try {
|
try {
|
||||||
mWriter = new BufferedWriter(new OutputStreamWriter(
|
mWriter = new BufferedWriter(new OutputStreamWriter(
|
||||||
mOutputStream, mCharsetString));
|
mOutputStream, mCharset));
|
||||||
} catch (UnsupportedEncodingException e1) {
|
} 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!): "
|
mErrorReason = "Encoding is not supported (usually this does not happen!): "
|
||||||
+ mCharsetString;
|
+ mCharset;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,14 +237,19 @@ public class VCardComposer {
|
|||||||
"IOException during closing the output stream: "
|
"IOException during closing the output stream: "
|
||||||
+ e.getMessage());
|
+ e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
closeOutputStream();
|
||||||
mWriter.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void closeOutputStream() {
|
||||||
|
try {
|
||||||
|
mWriter.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void finalize() {
|
public void finalize() {
|
||||||
if (!mOnTerminateIsCalled) {
|
if (!mOnTerminateIsCalled) {
|
||||||
@@ -257,11 +264,10 @@ public class VCardComposer {
|
|||||||
private final ContentResolver mContentResolver;
|
private final ContentResolver mContentResolver;
|
||||||
|
|
||||||
private final boolean mIsDoCoMo;
|
private final boolean mIsDoCoMo;
|
||||||
private final boolean mUsesShiftJis;
|
|
||||||
private Cursor mCursor;
|
private Cursor mCursor;
|
||||||
private int mIdColumn;
|
private int mIdColumn;
|
||||||
|
|
||||||
private final String mCharsetString;
|
private final String mCharset;
|
||||||
private boolean mTerminateIsCalled;
|
private boolean mTerminateIsCalled;
|
||||||
private final List<OneEntryHandler> mHandlerList;
|
private final List<OneEntryHandler> mHandlerList;
|
||||||
|
|
||||||
@@ -272,21 +278,39 @@ public class VCardComposer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public VCardComposer(Context context) {
|
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) {
|
public VCardComposer(Context context, int vcardType) {
|
||||||
this(context, vcardType, true);
|
this(context, vcardType, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) {
|
public VCardComposer(Context context, int vcardType, String charset) {
|
||||||
this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors);
|
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.
|
* 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) {
|
final boolean careHandlerErrors) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mVCardType = vcardType;
|
mVCardType = vcardType;
|
||||||
@@ -294,30 +318,67 @@ public class VCardComposer {
|
|||||||
mContentResolver = context.getContentResolver();
|
mContentResolver = context.getContentResolver();
|
||||||
|
|
||||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||||
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
|
|
||||||
mHandlerList = new ArrayList<OneEntryHandler>();
|
mHandlerList = new ArrayList<OneEntryHandler>();
|
||||||
|
|
||||||
if (mIsDoCoMo) {
|
charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset);
|
||||||
String charset;
|
final boolean shouldAppendCharsetParam = !(
|
||||||
try {
|
VCardConfig.isVersion30(vcardType) && UTF_8.equalsIgnoreCase(charset));
|
||||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
|
||||||
} catch (UnsupportedCharsetException e) {
|
if (mIsDoCoMo || shouldAppendCharsetParam) {
|
||||||
Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
if (SHIFT_JIS.equalsIgnoreCase(charset)) {
|
||||||
charset = SHIFT_JIS;
|
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 {
|
} 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) {
|
if (mCareHandlerErrors) {
|
||||||
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||||
mHandlerList.size());
|
mHandlerList.size());
|
||||||
for (OneEntryHandler handler : mHandlerList) {
|
for (OneEntryHandler handler : mHandlerList) {
|
||||||
if (!handler.onInit(mContext)) {
|
if (!handler.onInit(mContext)) {
|
||||||
@@ -414,7 +475,7 @@ public class VCardComposer {
|
|||||||
mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
|
mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String vcard;
|
final String vcard;
|
||||||
try {
|
try {
|
||||||
if (mIdColumn >= 0) {
|
if (mIdColumn >= 0) {
|
||||||
vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
|
vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
|
||||||
@@ -437,8 +498,7 @@ public class VCardComposer {
|
|||||||
mCursor.moveToNext();
|
mCursor.moveToNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function does not care the OutOfMemoryError on the handler side
|
// This function does not care the OutOfMemoryError on the handler side :-P
|
||||||
// :-P
|
|
||||||
if (mCareHandlerErrors) {
|
if (mCareHandlerErrors) {
|
||||||
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||||
mHandlerList.size());
|
mHandlerList.size());
|
||||||
@@ -457,7 +517,7 @@ public class VCardComposer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String createOneEntryInternal(final String contactId,
|
private String createOneEntryInternal(final String contactId,
|
||||||
Method getEntityIteratorMethod) throws VCardException {
|
final Method getEntityIteratorMethod) throws VCardException {
|
||||||
final Map<String, List<ContentValues>> contentValuesListMap =
|
final Map<String, List<ContentValues>> contentValuesListMap =
|
||||||
new HashMap<String, List<ContentValues>>();
|
new HashMap<String, List<ContentValues>>();
|
||||||
// The resolver may return the entity iterator with no data. It is possible.
|
// The resolver may return the entity iterator with no data. It is possible.
|
||||||
@@ -466,12 +526,13 @@ public class VCardComposer {
|
|||||||
EntityIterator entityIterator = null;
|
EntityIterator entityIterator = null;
|
||||||
try {
|
try {
|
||||||
final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon()
|
final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon()
|
||||||
|
// .appendQueryParameter("for_export_only", "1")
|
||||||
.appendQueryParameter(Data.FOR_EXPORT_ONLY, "1")
|
.appendQueryParameter(Data.FOR_EXPORT_ONLY, "1")
|
||||||
.build();
|
.build();
|
||||||
final String selection = Data.CONTACT_ID + "=?";
|
final String selection = Data.CONTACT_ID + "=?";
|
||||||
final String[] selectionArgs = new String[] {contactId};
|
final String[] selectionArgs = new String[] {contactId};
|
||||||
if (getEntityIteratorMethod != null) {
|
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 {
|
try {
|
||||||
entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
|
entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
|
||||||
mContentResolver, uri, selection, selectionArgs, null);
|
mContentResolver, uri, selection, selectionArgs, null);
|
||||||
@@ -527,22 +588,33 @@ public class VCardComposer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final VCardBuilder builder = new VCardBuilder(mVCardType);
|
return buildVCard(contentValuesListMap);
|
||||||
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))
|
* Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in
|
||||||
.appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
|
* {ContactsContract}. Developers can override this method to customize the output.
|
||||||
.appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
|
*/
|
||||||
.appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE));
|
public String buildVCard(final Map<String, List<ContentValues>> contentValuesListMap) {
|
||||||
if ((mVCardType & VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT) == 0) {
|
if (contentValuesListMap == null) {
|
||||||
builder.appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE));
|
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() {
|
public void terminate() {
|
||||||
@@ -565,26 +637,38 @@ public class VCardComposer {
|
|||||||
@Override
|
@Override
|
||||||
public void finalize() {
|
public void finalize() {
|
||||||
if (!mTerminateIsCalled) {
|
if (!mTerminateIsCalled) {
|
||||||
|
Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step.");
|
||||||
terminate();
|
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() {
|
public int getCount() {
|
||||||
if (mCursor == null) {
|
if (mCursor == null) {
|
||||||
|
Log.w(LOG_TAG, "This object is not ready yet.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return mCursor.getCount();
|
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() {
|
public boolean isAfterLast() {
|
||||||
if (mCursor == null) {
|
if (mCursor == null) {
|
||||||
|
Log.w(LOG_TAG, "This object is not ready yet.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return mCursor.isAfterLast();
|
return mCursor.isAfterLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Return the error reason if possible.
|
* @return Returns the error reason.
|
||||||
*/
|
*/
|
||||||
public String getErrorReason() {
|
public String getErrorReason() {
|
||||||
return mErrorReason;
|
return mErrorReason;
|
||||||
|
|||||||
@@ -38,20 +38,43 @@ public class VCardConfig {
|
|||||||
|
|
||||||
/* package */ static final int LOG_LEVEL = LOG_LEVEL_NONE;
|
/* 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;
|
* <p>
|
||||||
/* package */ static final int PARSE_TYPE_MOBILE_PHONE_JP = 2; // For Japanese mobile phones.
|
* The charset used during import.
|
||||||
/* package */ static final int PARSE_TYPE_FOMA = 3; // For Japanese FOMA mobile phones.
|
* </p>
|
||||||
/* package */ static final int PARSE_TYPE_WINDOWS_MOBILE_JP = 4;
|
* <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.
|
* The charset used when there's no information affbout what charset should be used to
|
||||||
public static final String DEFAULT_CHARSET = "iso-8859-1";
|
* encode the binary given from vCard.
|
||||||
|
*/
|
||||||
public static final int FLAG_V21 = 0;
|
public static final String DEFAULT_IMPORT_CHARSET = "UTF-8";
|
||||||
public static final int FLAG_V30 = 1;
|
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_DEFAULT = 0;
|
||||||
public static final int NAME_ORDER_EUROPE = 0x4;
|
public static final int NAME_ORDER_EUROPE = 0x4;
|
||||||
@@ -59,144 +82,140 @@ public class VCardConfig {
|
|||||||
private static final int NAME_ORDER_MASK = 0xC;
|
private static final int NAME_ORDER_MASK = 0xC;
|
||||||
|
|
||||||
// 0x10 is reserved for safety
|
// 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
|
* 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.
|
* 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.
|
* 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")
|
* When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME")
|
||||||
* instead of just dropping it.
|
* instead of just dropping it.
|
||||||
*
|
* </p>
|
||||||
|
* <p>
|
||||||
* vCard parser code automatically parses the field emitted even when this flag is off.
|
* vCard parser code automatically parses the field emitted even when this flag is off.
|
||||||
*
|
* </p>
|
||||||
* Note that this flag does not assure all the information must be hold in the emitted vCard.
|
|
||||||
*/
|
*/
|
||||||
private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000;
|
private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <p>
|
||||||
* The flag indicating the vCard composer will add some "X-" properties seen in the
|
* 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
|
* 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
|
* 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
|
* 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
|
* non-Android devices/softwares. We chose to enable the vCard composer to use those
|
||||||
* defact properties since they are also useful for Android devices.
|
* 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
|
* 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
|
* 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.
|
* 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;
|
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
|
* 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
|
* 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.
|
* dialect but the name order should be European", but it is not recommended.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
private static final int FLAG_DOCOMO = 0x20000000;
|
private static final int FLAG_DOCOMO = 0x20000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <p>
|
||||||
* The flag indicating the vCard composer does "NOT" use Quoted-Printable toward "primary"
|
* 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).
|
* 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
|
* 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
|
* in vCard specification either. Also be aware that it is NOT related to "primary" notion
|
||||||
* used in {@link android.provider.ContactsContract}.
|
* used in {@link android.provider.ContactsContract}.
|
||||||
* This notion is just for vCard composition in Android.
|
* 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
|
* 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.
|
* 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
|
* even when their values contain non-ascii or/and CR/LF, while they use the encoding in the
|
||||||
* other properties like "ADR", "ORG", etc.
|
* 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
|
* We are afraid of the case where some vCard importer also forget handling QP presuming QP is
|
||||||
* not used in such fields.
|
* 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
|
* This flag is useful when some target importer you are going to focus on does not accept
|
||||||
* such properties with Quoted-Printable encoding.
|
* 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.
|
* 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
|
* In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
|
||||||
* kind of problem (hopefully).
|
* kind of problem (hopefully).
|
||||||
* </P>
|
* </p>
|
||||||
|
* @hide
|
||||||
*/
|
*/
|
||||||
public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000;
|
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
|
* The flag indicating that phonetic name related fields must be converted to
|
||||||
* appropriate form. Note that "appropriate" is not defined in any vCard specification.
|
* appropriate form. Note that "appropriate" is not defined in any vCard specification.
|
||||||
* This is Android-specific.
|
* This is Android-specific.
|
||||||
* </P>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* One typical (and currently sole) example where we need this flag is the time when
|
* 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
|
* 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
|
* 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
|
* 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
|
* historical reasons, while the vCard importers embedded to softwares for PC should be
|
||||||
* able to parse them as we expect.
|
* 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
|
* 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.
|
* 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.
|
* In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in vCard 3.0 specification.
|
||||||
* </P>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* Detail:
|
* Detail:
|
||||||
* How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0.
|
* How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0.
|
||||||
* </p>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* e.g.<BR />
|
* e.g.
|
||||||
* 1) Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."<BR />
|
* </p>
|
||||||
* 2) Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."<BR />
|
* <ol>
|
||||||
* 3) Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."<BR />
|
* <li>Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."</li>
|
||||||
* </P>
|
* <li>Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."</li>
|
||||||
* <P>
|
* <li>Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."</li>
|
||||||
* 2) had been the default of VCard exporter/importer in Android, but it is found that
|
* </ol>
|
||||||
* some external exporter is not able to parse the type format like 2) but only 3).
|
* <p>
|
||||||
* </P>
|
|
||||||
* <P>
|
|
||||||
* If you are targeting to the importer which cannot accept TYPE params without "TYPE="
|
* If you are targeting to the importer which cannot accept TYPE params without "TYPE="
|
||||||
* strings (which should be rare though), please use this flag.
|
* strings (which should be rare though), please use this flag.
|
||||||
* </P>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* Example usage: int vcardType = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
|
* Example usage:
|
||||||
* </P>
|
* <pre class="prettyprint">int type = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);</pre>
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
|
public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <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>
|
|
||||||
* The flag indicating the vCard composer does touch nothing toward phone number Strings
|
* The flag indicating the vCard composer does touch nothing toward phone number Strings
|
||||||
* but leave it as is.
|
* but leave it as is.
|
||||||
* </P>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* The vCard specifications mention nothing toward phone numbers, while some devices
|
* The vCard specifications mention nothing toward phone numbers, while some devices
|
||||||
* do (wrongly, but with innevitable reasons).
|
* do (wrongly, but with innevitable reasons).
|
||||||
* For example, there's a possibility Japanese mobile phones are expected to have
|
* For example, there's a possibility Japanese mobile phones are expected to have
|
||||||
@@ -207,187 +226,194 @@ public class VCardConfig {
|
|||||||
* becomes "111-222-3333").
|
* becomes "111-222-3333").
|
||||||
* Unfortunate side effect of that use was some control characters used in the other
|
* Unfortunate side effect of that use was some control characters used in the other
|
||||||
* areas may be badly affected by the formatting.
|
* areas may be badly affected by the formatting.
|
||||||
* </P>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* This flag disables that formatting, affecting both importer and exporter.
|
* 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.
|
* 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;
|
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. ////
|
//// The followings are VCard types available from importer/exporter. ////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <p>
|
||||||
* Generic vCard format with the vCard 2.1. Uses UTF-8 for the charset.
|
* The type indicating nothing. Used by {@link VCardSourceDetector} when it
|
||||||
* When composing a vCard entry, the US convension will be used toward formatting
|
* was not able to guess the exact vCard type.
|
||||||
* some values.
|
* </p>
|
||||||
* </P>
|
*/
|
||||||
* <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",
|
* 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.
|
* 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 =
|
public static final int VCARD_TYPE_V21_GENERIC =
|
||||||
(FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
|
(VERSION_21 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||||
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.
|
* 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.
|
* Not fully ready yet. Use with caution when you use this.
|
||||||
* </P>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final int VCARD_TYPE_V30_GENERIC_UTF8 =
|
public static final int VCARD_TYPE_V30_GENERIC =
|
||||||
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
|
(VERSION_30 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||||
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.
|
* 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")
|
* Currently, only name order is considered ("Prefix Middle Given Family Suffix")
|
||||||
* </P>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final int VCARD_TYPE_V21_EUROPE_UTF8 =
|
public static final int VCARD_TYPE_V21_EUROPE =
|
||||||
(FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
|
(VERSION_21 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
|
||||||
|
/* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
|
||||||
/* package */ static final String VCARD_TYPE_V21_EUROPE_UTF8_STR = "v21_europe";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <p>
|
||||||
* General vCard format with the version 3.0 with some Europe convension. Uses UTF-8.
|
* 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.
|
* Not ready yet. Use with caution when you use this.
|
||||||
* </P>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final int VCARD_TYPE_V30_EUROPE_UTF8 =
|
public static final int VCARD_TYPE_V30_EUROPE =
|
||||||
(FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
|
(VERSION_30 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
|
||||||
|
|
||||||
/* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
|
/* 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.
|
* 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.
|
* Not ready yet. Use with caution when you use this.
|
||||||
* </P>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final int VCARD_TYPE_V21_JAPANESE_UTF8 =
|
public static final int VCARD_TYPE_V21_JAPANESE =
|
||||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
|
(VERSION_21 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||||
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>
|
* <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>
|
|
||||||
* The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
|
* 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.
|
* Not ready yet. Use with caution when you use this.
|
||||||
* </P>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
|
public static final int VCARD_TYPE_V30_JAPANESE =
|
||||||
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
|
(VERSION_30 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||||
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
|
* 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
|
* mobile phones, where phonetic names are translated to half-width katakana if
|
||||||
* possible, etc.
|
* possible, etc. It would be better to use Shift_JIS as a charset for maximum
|
||||||
* </P>
|
* compatibility.
|
||||||
* <P>
|
* </p>
|
||||||
* Not ready yet. Use with caution when you use this.
|
* @hide Should not be available world wide.
|
||||||
* </P>
|
|
||||||
*/
|
*/
|
||||||
public static final int VCARD_TYPE_V21_JAPANESE_MOBILE =
|
public static final int VCARD_TYPE_V21_JAPANESE_MOBILE =
|
||||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
|
(VERSION_21 | NAME_ORDER_JAPANESE |
|
||||||
FLAG_CONVERT_PHONETIC_NAME_STRINGS |
|
FLAG_CONVERT_PHONETIC_NAME_STRINGS | FLAG_REFRAIN_QP_TO_NAME_PROPERTIES);
|
||||||
FLAG_REFRAIN_QP_TO_NAME_PROPERTIES);
|
|
||||||
|
|
||||||
/* package */ static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile";
|
/* package */ static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <p>
|
||||||
* VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
|
* The vCard format used in DoCoMo, which is one of Japanese mobile phone careers.
|
||||||
* </p>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
|
* 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
|
* No Android-specific property nor defact property is included. The "Primary" properties
|
||||||
* are NOT encoded to Quoted-Printable.
|
* are NOT encoded to Quoted-Printable.
|
||||||
* </P>
|
* </p>
|
||||||
|
* @hide Should not be available world wide.
|
||||||
*/
|
*/
|
||||||
public static final int VCARD_TYPE_DOCOMO =
|
public static final int VCARD_TYPE_DOCOMO =
|
||||||
(VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
|
(VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
|
||||||
|
|
||||||
/* package */ static final String VCARD_TYPE_DOCOMO_STR = "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 Map<String, Integer> sVCardTypeMap;
|
||||||
private static final Set<Integer> sJapaneseMobileTypeSet;
|
private static final Set<Integer> sJapaneseMobileTypeSet;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
sVCardTypeMap = new HashMap<String, Integer>();
|
sVCardTypeMap = new HashMap<String, Integer>();
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_UTF8_STR, VCARD_TYPE_V21_GENERIC_UTF8);
|
sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_UTF8_STR, VCARD_TYPE_V30_GENERIC_UTF8);
|
sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_UTF8_STR, VCARD_TYPE_V21_EUROPE_UTF8);
|
sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE_UTF8);
|
sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_SJIS_STR, VCARD_TYPE_V21_JAPANESE_SJIS);
|
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
|
sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_SJIS_STR, VCARD_TYPE_V30_JAPANESE_SJIS);
|
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
|
|
||||||
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE);
|
sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE);
|
||||||
sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
|
sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
|
||||||
|
|
||||||
sJapaneseMobileTypeSet = new HashSet<Integer>();
|
sJapaneseMobileTypeSet = new HashSet<Integer>();
|
||||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_SJIS);
|
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE);
|
||||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_UTF8);
|
sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE);
|
||||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_SJIS);
|
|
||||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_SJIS);
|
|
||||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_UTF8);
|
|
||||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
|
sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
|
||||||
sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
|
sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
|
||||||
}
|
}
|
||||||
@@ -404,20 +430,20 @@ public class VCardConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isV30(final int vcardType) {
|
public static boolean isVersion21(final int vcardType) {
|
||||||
return ((vcardType & FLAG_V30) != 0);
|
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) {
|
public static boolean shouldUseQuotedPrintable(final int vcardType) {
|
||||||
return !isV30(vcardType);
|
return !isVersion30(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getNameOrderType(final int vcardType) {
|
public static int getNameOrderType(final int vcardType) {
|
||||||
@@ -442,7 +468,7 @@ public class VCardConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean appendTypeParamName(final int vcardType) {
|
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() {
|
private VCardConfig() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ package android.pim.vcard;
|
|||||||
public class VCardConstants {
|
public class VCardConstants {
|
||||||
public static final String VERSION_V21 = "2.1";
|
public static final String VERSION_V21 = "2.1";
|
||||||
public static final String VERSION_V30 = "3.0";
|
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.
|
// The property names valid both in vCard 2.1 and 3.0.
|
||||||
public static final String PROPERTY_BEGIN = "BEGIN";
|
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_PHOTO = "PHOTO";
|
||||||
public static final String PROPERTY_LOGO = "LOGO";
|
public static final String PROPERTY_LOGO = "LOGO";
|
||||||
public static final String PROPERTY_URL = "URL";
|
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";
|
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_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.
|
// 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.
|
// 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_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_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
|
||||||
public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
|
public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
|
||||||
|
|
||||||
// Properties both ContactsStruct in Eclair and de-fact vCard extensions
|
// Properties both ContactsStruct and de-fact vCard extensions
|
||||||
// shown in http://en.wikipedia.org/wiki/VCard support are defined here.
|
// 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_AIM = "X-AIM";
|
||||||
public static final String PROPERTY_X_MSN = "X-MSN";
|
public static final String PROPERTY_X_MSN = "X-MSN";
|
||||||
public static final String PROPERTY_X_YAHOO = "X-YAHOO";
|
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_VOICE = "VOICE";
|
||||||
public static final String PARAM_TYPE_INTERNET = "INTERNET";
|
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.
|
// Abbreviation of "prefered" according to vCard 2.1 specification.
|
||||||
// We interpret this value as "primary" property during import/export.
|
// 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_BBS = "BBS";
|
||||||
public static final String PARAM_TYPE_VIDEO = "VIDEO";
|
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).
|
// 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.
|
// These types are basically encoded to "X-" parameters when composing vCard.
|
||||||
// Parser passes these when "X-" is added to the parameter or not.
|
// 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_DOM = "DOM";
|
||||||
public static final String PARAM_ADR_TYPE_INTL = "INTL";
|
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.
|
// TYPE parameters not officially valid but used in some vCard exporter.
|
||||||
// Do not use in composer side.
|
// Do not use in composer side.
|
||||||
public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY";
|
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 interface ImportOnly {
|
||||||
public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
|
public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
|
||||||
// Some device emits this "X-" parameter for expressing Google Talk,
|
// 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";
|
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;
|
/* package */ static final int MAX_CHARACTER_NUMS_QP = 76;
|
||||||
static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75;
|
static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75;
|
||||||
|
|||||||
@@ -20,13 +20,11 @@ import android.content.ContentProviderOperation;
|
|||||||
import android.content.ContentProviderResult;
|
import android.content.ContentProviderResult;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.OperationApplicationException;
|
import android.content.OperationApplicationException;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.provider.ContactsContract.Contacts;
|
import android.provider.ContactsContract.Contacts;
|
||||||
import android.provider.ContactsContract.Data;
|
import android.provider.ContactsContract.Data;
|
||||||
import android.provider.ContactsContract.Groups;
|
|
||||||
import android.provider.ContactsContract.RawContacts;
|
import android.provider.ContactsContract.RawContacts;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||||
@@ -61,9 +59,6 @@ public class VCardEntry {
|
|||||||
|
|
||||||
private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
|
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>();
|
private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -78,12 +73,12 @@ public class VCardEntry {
|
|||||||
Im.PROTOCOL_GOOGLE_TALK);
|
Im.PROTOCOL_GOOGLE_TALK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class PhoneData {
|
public static class PhoneData {
|
||||||
public final int type;
|
public final int type;
|
||||||
public final String data;
|
public final String data;
|
||||||
public final String label;
|
public final String label;
|
||||||
// isPrimary is changable only when there's no appropriate one existing in
|
// isPrimary is (not final but) changable, only when there's no appropriate one existing
|
||||||
// the original VCard.
|
// in the original VCard.
|
||||||
public boolean isPrimary;
|
public boolean isPrimary;
|
||||||
public PhoneData(int type, String data, String label, boolean isPrimary) {
|
public PhoneData(int type, String data, String label, boolean isPrimary) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -109,13 +104,11 @@ public class VCardEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class EmailData {
|
public static class EmailData {
|
||||||
public final int type;
|
public final int type;
|
||||||
public final String data;
|
public final String data;
|
||||||
// Used only when TYPE is TYPE_CUSTOM.
|
// Used only when TYPE is TYPE_CUSTOM.
|
||||||
public final String label;
|
public final String label;
|
||||||
// isPrimary is changable only when there's no appropriate one existing in
|
|
||||||
// the original VCard.
|
|
||||||
public boolean isPrimary;
|
public boolean isPrimary;
|
||||||
public EmailData(int type, String data, String label, boolean isPrimary) {
|
public EmailData(int type, String data, String label, boolean isPrimary) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -141,9 +134,9 @@ public class VCardEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class PostalData {
|
public static class PostalData {
|
||||||
// Determined by vCard spec.
|
// Determined by vCard specification.
|
||||||
// PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
|
// - PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
|
||||||
public static final int ADDR_MAX_DATA_SIZE = 7;
|
public static final int ADDR_MAX_DATA_SIZE = 7;
|
||||||
private final String[] dataArray;
|
private final String[] dataArray;
|
||||||
public final String pobox;
|
public final String pobox;
|
||||||
@@ -248,24 +241,28 @@ public class VCardEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class OrganizationData {
|
public static class OrganizationData {
|
||||||
public final int type;
|
public final int type;
|
||||||
// non-final is Intentional: we may change the values since this info is separated into
|
// 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 companyName;
|
||||||
public String departmentName;
|
public String departmentName;
|
||||||
public String titleName;
|
public String titleName;
|
||||||
|
public final String phoneticName; // We won't have this in "TITLE" property.
|
||||||
public boolean isPrimary;
|
public boolean isPrimary;
|
||||||
|
|
||||||
public OrganizationData(int type,
|
public OrganizationData(int type,
|
||||||
String companyName,
|
final String companyName,
|
||||||
String departmentName,
|
final String departmentName,
|
||||||
String titleName,
|
final String titleName,
|
||||||
boolean isPrimary) {
|
final String phoneticName,
|
||||||
|
final boolean isPrimary) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.companyName = companyName;
|
this.companyName = companyName;
|
||||||
this.departmentName = departmentName;
|
this.departmentName = departmentName;
|
||||||
this.titleName = titleName;
|
this.titleName = titleName;
|
||||||
|
this.phoneticName = phoneticName;
|
||||||
this.isPrimary = isPrimary;
|
this.isPrimary = isPrimary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +310,7 @@ public class VCardEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class ImData {
|
public static class ImData {
|
||||||
public final int protocol;
|
public final int protocol;
|
||||||
public final String customProtocol;
|
public final String customProtocol;
|
||||||
public final int type;
|
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 mFamilyName;
|
||||||
private String mGivenName;
|
private String mGivenName;
|
||||||
private String mMiddleName;
|
private String mMiddleName;
|
||||||
@@ -441,7 +446,7 @@ public class VCardEntry {
|
|||||||
private String mSuffix;
|
private String mSuffix;
|
||||||
|
|
||||||
// Used only when no family nor given name is found.
|
// Used only when no family nor given name is found.
|
||||||
private String mFullName;
|
private String mFormattedName;
|
||||||
|
|
||||||
private String mPhoneticFamilyName;
|
private String mPhoneticFamilyName;
|
||||||
private String mPhoneticGivenName;
|
private String mPhoneticGivenName;
|
||||||
@@ -454,6 +459,7 @@ public class VCardEntry {
|
|||||||
private String mDisplayName;
|
private String mDisplayName;
|
||||||
|
|
||||||
private String mBirthday;
|
private String mBirthday;
|
||||||
|
private String mAnniversary;
|
||||||
|
|
||||||
private List<String> mNoteList;
|
private List<String> mNoteList;
|
||||||
private List<PhoneData> mPhoneList;
|
private List<PhoneData> mPhoneList;
|
||||||
@@ -469,7 +475,7 @@ public class VCardEntry {
|
|||||||
private final Account mAccount;
|
private final Account mAccount;
|
||||||
|
|
||||||
public VCardEntry() {
|
public VCardEntry() {
|
||||||
this(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
|
this(VCardConfig.VCARD_TYPE_V21_GENERIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardEntry(int vcardType) {
|
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);
|
final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
|
||||||
formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
|
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)}.
|
* {@link #handleTitleValue(String)}.
|
||||||
*/
|
*/
|
||||||
private void addNewOrganization(int type, final String companyName,
|
private void addNewOrganization(int type, final String companyName,
|
||||||
final String departmentName,
|
final String departmentName,
|
||||||
final String titleName, boolean isPrimary) {
|
final String titleName,
|
||||||
|
final String phoneticName,
|
||||||
|
final boolean isPrimary) {
|
||||||
if (mOrganizationList == null) {
|
if (mOrganizationList == null) {
|
||||||
mOrganizationList = new ArrayList<OrganizationData>();
|
mOrganizationList = new ArrayList<OrganizationData>();
|
||||||
}
|
}
|
||||||
mOrganizationList.add(new OrganizationData(type, companyName,
|
mOrganizationList.add(new OrganizationData(type, companyName,
|
||||||
departmentName, titleName, isPrimary));
|
departmentName, titleName, phoneticName, isPrimary));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final List<String> sEmptyList =
|
private static final List<String> sEmptyList =
|
||||||
Collections.unmodifiableList(new ArrayList<String>(0));
|
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
|
* 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
|
* {@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,
|
* {@link OrganizationData} object, a new {@link OrganizationData} is created,
|
||||||
* whose title is set to null.
|
* 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) {
|
if (orgList == null) {
|
||||||
orgList = sEmptyList;
|
orgList = sEmptyList;
|
||||||
}
|
}
|
||||||
@@ -587,7 +616,7 @@ public class VCardEntry {
|
|||||||
if (mOrganizationList == null) {
|
if (mOrganizationList == null) {
|
||||||
// Create new first organization entry, with "null" title which may be
|
// Create new first organization entry, with "null" title which may be
|
||||||
// added via handleTitleValue().
|
// added via handleTitleValue().
|
||||||
addNewOrganization(type, companyName, departmentName, null, isPrimary);
|
addNewOrganization(type, companyName, departmentName, null, phoneticName, isPrimary);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (OrganizationData organizationData : mOrganizationList) {
|
for (OrganizationData organizationData : mOrganizationList) {
|
||||||
@@ -605,7 +634,7 @@ public class VCardEntry {
|
|||||||
}
|
}
|
||||||
// No OrganizatioData is available. Create another one, with "null" title, which may be
|
// No OrganizatioData is available. Create another one, with "null" title, which may be
|
||||||
// added via handleTitleValue().
|
// 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) {
|
if (mOrganizationList == null) {
|
||||||
// Create new first organization entry, with "null" other info, which may be
|
// Create new first organization entry, with "null" other info, which may be
|
||||||
// added via handleOrgValue().
|
// added via handleOrgValue().
|
||||||
addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false);
|
addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, null, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (OrganizationData organizationData : mOrganizationList) {
|
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
|
// No Organization is available. Create another one, with "null" other info, which may be
|
||||||
// added via handleOrgValue().
|
// 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,
|
private void addIm(int protocol, String customProtocol, int type,
|
||||||
@@ -656,11 +685,54 @@ public class VCardEntry {
|
|||||||
mPhotoList.add(photoData);
|
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")
|
@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)
|
// Family, Given, Middle, Prefix, Suffix. (1 - 5)
|
||||||
int size;
|
int size;
|
||||||
if (elems == null || (size = elems.size()) < 1) {
|
if (paramValues == null || (size = paramValues.size()) < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (size > 5) {
|
if (size > 5) {
|
||||||
@@ -668,12 +740,12 @@ public class VCardEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
// fallthrough
|
// Fall-through.
|
||||||
case 5: mSuffix = elems.get(4);
|
case 5: mSuffix = paramValues.get(4);
|
||||||
case 4: mPrefix = elems.get(3);
|
case 4: mPrefix = paramValues.get(3);
|
||||||
case 3: mMiddleName = elems.get(2);
|
case 3: mMiddleName = paramValues.get(2);
|
||||||
case 2: mGivenName = elems.get(1);
|
case 2: mGivenName = paramValues.get(1);
|
||||||
default: mFamilyName = elems.get(0);
|
default: mFamilyName = paramValues.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -754,13 +826,13 @@ public class VCardEntry {
|
|||||||
if (propName.equals(VCardConstants.PROPERTY_VERSION)) {
|
if (propName.equals(VCardConstants.PROPERTY_VERSION)) {
|
||||||
// vCard version. Ignore this.
|
// vCard version. Ignore this.
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_FN)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_FN)) {
|
||||||
mFullName = propValue;
|
mFormattedName = propValue;
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFullName == null) {
|
} 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
|
// 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.
|
// actually exist in the real vCard data, does not exist.
|
||||||
mFullName = propValue;
|
mFormattedName = propValue;
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_N)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_N)) {
|
||||||
handleNProperty(propValueList);
|
handleNProperty(propValueList, paramMap);
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
|
||||||
mPhoneticFullName = propValue;
|
mPhoneticFullName = propValue;
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_NICKNAME) ||
|
} 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.
|
// which is correct behavior from the view of vCard 2.1.
|
||||||
// But we want it to be separated, so do the separation here.
|
// But we want it to be separated, so do the separation here.
|
||||||
final List<String> phoneticNameList =
|
final List<String> phoneticNameList =
|
||||||
VCardUtils.constructListFromValue(propValue,
|
VCardUtils.constructListFromValue(propValue, mVCardType);
|
||||||
VCardConfig.isV30(mVCardType));
|
|
||||||
handlePhoneticNameFromSound(phoneticNameList);
|
handlePhoneticNameFromSound(phoneticNameList);
|
||||||
} else {
|
} else {
|
||||||
// Ignore this field since Android cannot understand what it is.
|
// 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)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_TITLE)) {
|
||||||
handleTitleValue(propValue);
|
handleTitleValue(propValue);
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_ROLE)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_ROLE)) {
|
||||||
@@ -966,6 +1037,8 @@ public class VCardEntry {
|
|||||||
mWebsiteList.add(propValue);
|
mWebsiteList.add(propValue);
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_BDAY)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_BDAY)) {
|
||||||
mBirthday = propValue;
|
mBirthday = propValue;
|
||||||
|
} else if (propName.equals(VCardConstants.PROPERTY_ANNIVERSARY)) {
|
||||||
|
mAnniversary = propValue;
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
|
||||||
mPhoneticGivenName = propValue;
|
mPhoneticGivenName = propValue;
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
|
||||||
@@ -974,33 +1047,9 @@ public class VCardEntry {
|
|||||||
mPhoneticFamilyName = propValue;
|
mPhoneticFamilyName = propValue;
|
||||||
} else if (propName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) {
|
} else if (propName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) {
|
||||||
final List<String> customPropertyList =
|
final List<String> customPropertyList =
|
||||||
VCardUtils.constructListFromValue(propValue,
|
VCardUtils.constructListFromValue(propValue, mVCardType);
|
||||||
VCardConfig.isV30(mVCardType));
|
|
||||||
handleAndroidCustomProperty(customPropertyList);
|
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 {
|
} else {
|
||||||
// Unknown X- words and IANA token.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,8 +1065,8 @@ public class VCardEntry {
|
|||||||
*/
|
*/
|
||||||
private void constructDisplayName() {
|
private void constructDisplayName() {
|
||||||
// FullName (created via "FN" or "NAME" field) is prefered.
|
// FullName (created via "FN" or "NAME" field) is prefered.
|
||||||
if (!TextUtils.isEmpty(mFullName)) {
|
if (!TextUtils.isEmpty(mFormattedName)) {
|
||||||
mDisplayName = mFullName;
|
mDisplayName = mFormattedName;
|
||||||
} else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
|
} else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
|
||||||
mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
|
mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
|
||||||
mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix);
|
mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix);
|
||||||
@@ -1062,23 +1111,6 @@ public class VCardEntry {
|
|||||||
if (mAccount != null) {
|
if (mAccount != null) {
|
||||||
builder.withValue(RawContacts.ACCOUNT_NAME, mAccount.name);
|
builder.withValue(RawContacts.ACCOUNT_NAME, mAccount.name);
|
||||||
builder.withValue(RawContacts.ACCOUNT_TYPE, mAccount.type);
|
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 {
|
} else {
|
||||||
builder.withValue(RawContacts.ACCOUNT_NAME, null);
|
builder.withValue(RawContacts.ACCOUNT_NAME, null);
|
||||||
builder.withValue(RawContacts.ACCOUNT_TYPE, null);
|
builder.withValue(RawContacts.ACCOUNT_TYPE, null);
|
||||||
@@ -1154,6 +1186,9 @@ public class VCardEntry {
|
|||||||
if (organizationData.titleName != null) {
|
if (organizationData.titleName != null) {
|
||||||
builder.withValue(Organization.TITLE, organizationData.titleName);
|
builder.withValue(Organization.TITLE, organizationData.titleName);
|
||||||
}
|
}
|
||||||
|
if (organizationData.phoneticName != null) {
|
||||||
|
builder.withValue(Organization.PHONETIC_NAME, organizationData.phoneticName);
|
||||||
|
}
|
||||||
if (organizationData.isPrimary) {
|
if (organizationData.isPrimary) {
|
||||||
builder.withValue(Organization.IS_PRIMARY, 1);
|
builder.withValue(Organization.IS_PRIMARY, 1);
|
||||||
}
|
}
|
||||||
@@ -1251,6 +1286,15 @@ public class VCardEntry {
|
|||||||
operationList.add(builder.build());
|
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) {
|
if (mAndroidCustomPropertyList != null) {
|
||||||
for (List<String> customPropertyList : mAndroidCustomPropertyList) {
|
for (List<String> customPropertyList : mAndroidCustomPropertyList) {
|
||||||
int size = customPropertyList.size();
|
int size = customPropertyList.size();
|
||||||
@@ -1322,7 +1366,7 @@ public class VCardEntry {
|
|||||||
&& TextUtils.isEmpty(mGivenName)
|
&& TextUtils.isEmpty(mGivenName)
|
||||||
&& TextUtils.isEmpty(mPrefix)
|
&& TextUtils.isEmpty(mPrefix)
|
||||||
&& TextUtils.isEmpty(mSuffix)
|
&& TextUtils.isEmpty(mSuffix)
|
||||||
&& TextUtils.isEmpty(mFullName)
|
&& TextUtils.isEmpty(mFormattedName)
|
||||||
&& TextUtils.isEmpty(mPhoneticFamilyName)
|
&& TextUtils.isEmpty(mPhoneticFamilyName)
|
||||||
&& TextUtils.isEmpty(mPhoneticMiddleName)
|
&& TextUtils.isEmpty(mPhoneticMiddleName)
|
||||||
&& TextUtils.isEmpty(mPhoneticGivenName)
|
&& TextUtils.isEmpty(mPhoneticGivenName)
|
||||||
@@ -1381,7 +1425,7 @@ public class VCardEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return mFullName;
|
return mFormattedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPhoneticFamilyName() {
|
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();
|
long start = System.currentTimeMillis();
|
||||||
mCreatedUris.add(contactStruct.pushIntoContentResolver(mContentResolver));
|
mCreatedUris.add(vcardEntry.pushIntoContentResolver(mContentResolver));
|
||||||
mTimeToCommit += System.currentTimeMillis() - start;
|
mTimeToCommit += System.currentTimeMillis() - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,78 +16,82 @@
|
|||||||
package android.pim.vcard;
|
package android.pim.vcard;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
import android.util.CharsetUtils;
|
import android.util.CharsetUtils;
|
||||||
import android.util.Log;
|
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.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
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 {
|
public class VCardEntryConstructor implements VCardInterpreter {
|
||||||
private static String LOG_TAG = "VCardEntryConstructor";
|
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.Property mCurrentProperty = new VCardEntry.Property();
|
||||||
private VCardEntry mCurrentContactStruct;
|
private VCardEntry mCurrentVCardEntry;
|
||||||
private String mParamType;
|
private String mParamType;
|
||||||
|
|
||||||
/**
|
// The charset using which {@link VCardInterpreter} parses the text.
|
||||||
* 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"
|
||||||
private String mInputCharset;
|
// property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8).
|
||||||
|
private final String mSourceCharset;
|
||||||
|
|
||||||
/**
|
private final boolean mStrictLineBreaking;
|
||||||
* The charset with which byte array is encoded to String.
|
private final int mVCardType;
|
||||||
*/
|
private final Account mAccount;
|
||||||
final private String mCharsetForDecodedBytes;
|
|
||||||
final private boolean mStrictLineBreakParsing;
|
|
||||||
final private int mVCardType;
|
|
||||||
final private Account mAccount;
|
|
||||||
|
|
||||||
/** For measuring performance. */
|
// For measuring performance.
|
||||||
private long mTimePushIntoContentResolver;
|
private long mTimePushIntoContentResolver;
|
||||||
|
|
||||||
final private List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
|
private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
|
||||||
|
|
||||||
public VCardEntryConstructor() {
|
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) {
|
public VCardEntryConstructor(final int vcardType) {
|
||||||
this(null, null, false, vcardType, null);
|
this(vcardType, null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardEntryConstructor(final String charset, final boolean strictLineBreakParsing,
|
public VCardEntryConstructor(final int vcardType, final Account account) {
|
||||||
final int vcardType, final Account account) {
|
this(vcardType, account, null, false);
|
||||||
this(null, charset, strictLineBreakParsing, vcardType, account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardEntryConstructor(final String inputCharset, final String charsetForDetodedBytes,
|
public VCardEntryConstructor(final int vcardType, final Account account,
|
||||||
final boolean strictLineBreakParsing, final int vcardType,
|
final String inputCharset) {
|
||||||
final Account account) {
|
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) {
|
if (inputCharset != null) {
|
||||||
mInputCharset = inputCharset;
|
mSourceCharset = inputCharset;
|
||||||
} else {
|
} else {
|
||||||
mInputCharset = VCardConfig.DEFAULT_CHARSET;
|
mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
|
||||||
}
|
}
|
||||||
if (charsetForDetodedBytes != null) {
|
mStrictLineBreaking = strictLineBreakParsing;
|
||||||
mCharsetForDecodedBytes = charsetForDetodedBytes;
|
|
||||||
} else {
|
|
||||||
mCharsetForDecodedBytes = DEFAULT_CHARSET_FOR_DECODED_BYTES;
|
|
||||||
}
|
|
||||||
mStrictLineBreakParsing = strictLineBreakParsing;
|
|
||||||
mVCardType = vcardType;
|
mVCardType = vcardType;
|
||||||
mAccount = account;
|
mAccount = account;
|
||||||
}
|
}
|
||||||
@@ -95,60 +99,63 @@ public class VCardEntryConstructor implements VCardInterpreter {
|
|||||||
public void addEntryHandler(VCardEntryHandler entryHandler) {
|
public void addEntryHandler(VCardEntryHandler entryHandler) {
|
||||||
mEntryHandlers.add(entryHandler);
|
mEntryHandlers.add(entryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||||
entryHandler.onStart();
|
entryHandler.onStart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void end() {
|
public void end() {
|
||||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||||
entryHandler.onEnd();
|
entryHandler.onEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the parse failed between {@link #startEntry()} and {@link #endEntry()}.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
mCurrentContactStruct = null;
|
mCurrentVCardEntry = null;
|
||||||
mCurrentProperty = new VCardEntry.Property();
|
mCurrentProperty = new VCardEntry.Property();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Assume that VCard is not nested. In other words, this code does not accept
|
|
||||||
*/
|
|
||||||
public void startEntry() {
|
public void startEntry() {
|
||||||
if (mCurrentContactStruct != null) {
|
if (mCurrentVCardEntry != null) {
|
||||||
Log.e(LOG_TAG, "Nested VCard code is not supported now.");
|
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() {
|
public void endEntry() {
|
||||||
mCurrentContactStruct.consolidateFields();
|
mCurrentVCardEntry.consolidateFields();
|
||||||
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
for (VCardEntryHandler entryHandler : mEntryHandlers) {
|
||||||
entryHandler.onEntryCreated(mCurrentContactStruct);
|
entryHandler.onEntryCreated(mCurrentVCardEntry);
|
||||||
}
|
}
|
||||||
mCurrentContactStruct = null;
|
mCurrentVCardEntry = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void startProperty() {
|
public void startProperty() {
|
||||||
mCurrentProperty.clear();
|
mCurrentProperty.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void endProperty() {
|
public void endProperty() {
|
||||||
mCurrentContactStruct.addProperty(mCurrentProperty);
|
mCurrentVCardEntry.addProperty(mCurrentProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void propertyName(String name) {
|
public void propertyName(String name) {
|
||||||
mCurrentProperty.setPropertyName(name);
|
mCurrentProperty.setPropertyName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void propertyGroup(String group) {
|
public void propertyGroup(String group) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void propertyParamType(String type) {
|
public void propertyParamType(String type) {
|
||||||
if (mParamType != null) {
|
if (mParamType != null) {
|
||||||
Log.e(LOG_TAG, "propertyParamType() is called more than once " +
|
Log.e(LOG_TAG, "propertyParamType() is called more than once " +
|
||||||
@@ -164,119 +171,34 @@ public class VCardEntryConstructor implements VCardInterpreter {
|
|||||||
mParamType = "TYPE";
|
mParamType = "TYPE";
|
||||||
}
|
}
|
||||||
if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
|
if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
|
||||||
value = encodeString(value, mCharsetForDecodedBytes);
|
value = VCardUtils.convertStringCharset(
|
||||||
|
value, mSourceCharset, VCardConfig.DEFAULT_IMPORT_CHARSET);
|
||||||
}
|
}
|
||||||
mCurrentProperty.addParameter(mParamType, value);
|
mCurrentProperty.addParameter(mParamType, value);
|
||||||
mParamType = null;
|
mParamType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String encodeString(String originalString, String charsetForDecodedBytes) {
|
private String handleOneValue(String value,
|
||||||
if (mInputCharset.equalsIgnoreCase(charsetForDecodedBytes)) {
|
String sourceCharset, String targetCharset, String encoding) {
|
||||||
return originalString;
|
// 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 != null) {
|
||||||
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
||||||
mCurrentProperty.setPropertyBytes(Base64.decodeBase64(value.getBytes()));
|
mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT));
|
||||||
return value;
|
return value;
|
||||||
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
||||||
// "= " -> " ", "=\t" -> "\t".
|
return VCardUtils.parseQuotedPrintable(
|
||||||
// Previous code had done this replacement. Keep on the safe side.
|
value, mStrictLineBreaking, sourceCharset, targetCharset);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 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) {
|
public void propertyValues(List<String> values) {
|
||||||
@@ -284,24 +206,27 @@ public class VCardEntryConstructor implements VCardInterpreter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET");
|
final Collection<String> charsetCollection =
|
||||||
final String charset =
|
mCurrentProperty.getParameters(VCardConstants.PARAM_CHARSET);
|
||||||
((charsetCollection != null) ? charsetCollection.iterator().next() : null);
|
final Collection<String> encodingCollection =
|
||||||
final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING");
|
mCurrentProperty.getParameters(VCardConstants.PARAM_ENCODING);
|
||||||
final String encoding =
|
final String encoding =
|
||||||
((encodingCollection != null) ? encodingCollection.iterator().next() : null);
|
((encodingCollection != null) ? encodingCollection.iterator().next() : null);
|
||||||
|
String targetCharset = CharsetUtils.nameForDefaultVendor(
|
||||||
String charsetForDecodedBytes = CharsetUtils.nameForDefaultVendor(charset);
|
((charsetCollection != null) ? charsetCollection.iterator().next() : null));
|
||||||
if (charsetForDecodedBytes == null || charsetForDecodedBytes.length() == 0) {
|
if (TextUtils.isEmpty(targetCharset)) {
|
||||||
charsetForDecodedBytes = mCharsetForDecodedBytes;
|
targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final String value : values) {
|
for (final String value : values) {
|
||||||
mCurrentProperty.addToPropertyValueList(
|
mCurrentProperty.addToPropertyValueList(
|
||||||
handleOneValue(value, charsetForDecodedBytes, encoding));
|
handleOneValue(value, mSourceCharset, targetCharset, encoding));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
public void showPerformanceInfo() {
|
public void showPerformanceInfo() {
|
||||||
Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
|
Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
|
||||||
mTimePushIntoContentResolver + " ms");
|
mTimePushIntoContentResolver + " ms");
|
||||||
|
|||||||
@@ -16,8 +16,13 @@
|
|||||||
package android.pim.vcard;
|
package android.pim.vcard;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface called by {@link VCardEntryConstructor}. Useful when you don't want to
|
* <p>
|
||||||
* handle detailed information as what {@link VCardParser} provides via {@link VCardInterpreter}.
|
* 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 {
|
public interface VCardEntryHandler {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* <P>
|
* <P>
|
||||||
* The interface which should be implemented by the classes which have to analyze each
|
* 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>
|
||||||
* <P>
|
* <P>
|
||||||
* Here, there are several terms specific to vCard (and this library).
|
* 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} objects and make a user object treat them as one
|
||||||
* {@link VCardInterpreter} object.
|
* {@link VCardInterpreter} object.
|
||||||
*/
|
*/
|
||||||
public class VCardInterpreterCollection implements VCardInterpreter {
|
public final class VCardInterpreterCollection implements VCardInterpreter {
|
||||||
private final Collection<VCardInterpreter> mInterpreterCollection;
|
private final Collection<VCardInterpreter> mInterpreterCollection;
|
||||||
|
|
||||||
public VCardInterpreterCollection(Collection<VCardInterpreter> interpreterCollection) {
|
public VCardInterpreterCollection(Collection<VCardInterpreter> interpreterCollection) {
|
||||||
mInterpreterCollection = interpreterCollection;
|
mInterpreterCollection = interpreterCollection;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,82 +20,36 @@ import android.pim.vcard.exception.VCardException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public abstract class VCardParser {
|
public interface VCardParser {
|
||||||
protected final int mParseType;
|
|
||||||
protected boolean mCanceled;
|
|
||||||
|
|
||||||
public VCardParser() {
|
|
||||||
this(VCardConfig.PARSE_TYPE_UNKNOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VCardParser(int parseType) {
|
|
||||||
mParseType = parseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <p>
|
||||||
* Parses the given stream and send the VCard data into VCardBuilderBase object.
|
* Parses the given stream and send the vCard data into VCardBuilderBase object.
|
||||||
* </P.
|
* </p>.
|
||||||
* <P>
|
* <p>
|
||||||
* Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
|
* 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
|
* 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,
|
* formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1,
|
||||||
* In some exreme case, some VCard may have different charsets in one VCard (though
|
* In some exreme case, it is allowed for vCard to have different charsets in one vCard.
|
||||||
* we do not see any device which emits such kind of malicious data)
|
* </p>
|
||||||
* </P>
|
* <p>
|
||||||
* <P>
|
* We recommend you use {@link VCardSourceDetector} and detect which kind of source the
|
||||||
* In order to avoid "misunderstanding" charset as much as possible, this method
|
* vCard comes from and explicitly specify a charset using the result.
|
||||||
* use "ISO-8859-1" for reading the stream. When charset is specified in some property
|
* </p>
|
||||||
* (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>
|
|
||||||
*
|
*
|
||||||
* @param is The source to parse.
|
* @param is The source to parse.
|
||||||
* @param interepreter A {@link VCardInterpreter} object which used to construct data.
|
* @param interepreter A {@link VCardInterpreter} object which used to construct data.
|
||||||
* @return Returns true for success. Otherwise returns false.
|
|
||||||
* @throws IOException, VCardException
|
* @throws IOException, VCardException
|
||||||
*/
|
*/
|
||||||
public abstract boolean parse(InputStream is, VCardInterpreter interepreter)
|
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||||
throws IOException, VCardException;
|
throws IOException, VCardException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <p>
|
||||||
* The method variants which accept charset.
|
* Cancel parsing vCard. Useful when you want to stop the parse in the other threads.
|
||||||
* </P>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use
|
* Actual cancel is done after parsing the current vcard.
|
||||||
* UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese
|
* </p>
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
public abstract boolean parse(InputStream is, String charset, VCardInterpreter builder)
|
public abstract void cancel();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
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;
|
package android.pim.vcard;
|
||||||
|
|
||||||
import android.pim.vcard.exception.VCardAgentNotSupportedException;
|
|
||||||
import android.pim.vcard.exception.VCardException;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
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 {
|
public final class VCardParser_V21 implements VCardParser {
|
||||||
private static final String LOG_TAG = "VCardParser_V21";
|
/**
|
||||||
|
* A unmodifiable Set storing the property names available in the vCard 2.1 specification.
|
||||||
/** Store the known-type */
|
*/
|
||||||
private static final HashSet<String> sKnownTypeSet = new HashSet<String>(
|
/* package */ static final Set<String> sKnownPropertyNameSet =
|
||||||
Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
|
Collections.unmodifiableSet(new HashSet<String>(
|
||||||
"PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
|
Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
||||||
"MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
|
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
||||||
"ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
|
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER")));
|
||||||
"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"));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 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.
|
* 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 =
|
/* package */ static final Set<String> sAvailableEncoding =
|
||||||
new HashSet<String>(Arrays.asList(
|
Collections.unmodifiableSet(new HashSet<String>(
|
||||||
"7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
|
Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT,
|
||||||
|
VCardConstants.PARAM_ENCODING_8BIT,
|
||||||
// Used only for parsing END:VCARD.
|
VCardConstants.PARAM_ENCODING_QP,
|
||||||
private String mPreviousLine;
|
VCardConstants.PARAM_ENCODING_BASE64,
|
||||||
|
VCardConstants.PARAM_ENCODING_B)));
|
||||||
/** The builder to build parsed data */
|
|
||||||
protected VCardInterpreter mBuilder = null;
|
|
||||||
|
|
||||||
/**
|
private final VCardParserImpl_V21 mVCardParserImpl;
|
||||||
* 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;
|
|
||||||
|
|
||||||
public VCardParser_V21() {
|
public VCardParser_V21() {
|
||||||
this(null);
|
mVCardParserImpl = new VCardParserImpl_V21();
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardParser_V21(VCardSourceDetector detector) {
|
public VCardParser_V21(int vcardType) {
|
||||||
this(detector != null ? detector.getEstimatedType() : VCardConfig.PARSE_TYPE_UNKNOWN);
|
mVCardParserImpl = new VCardParserImpl_V21(vcardType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardParser_V21(int parseType) {
|
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||||
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)
|
|
||||||
throws IOException, VCardException {
|
throws IOException, VCardException {
|
||||||
String line;
|
mVCardParserImpl.parse(is, interepreter);
|
||||||
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 + "\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static private final int STATE_GROUP_OR_PROPNAME = 0;
|
public void cancel() {
|
||||||
static private final int STATE_PARAMS = 1;
|
mVCardParserImpl.cancel();
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,343 +16,72 @@
|
|||||||
package android.pim.vcard;
|
package android.pim.vcard;
|
||||||
|
|
||||||
import android.pim.vcard.exception.VCardException;
|
import android.pim.vcard.exception.VCardException;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class used to parse vCard 3.0.
|
* <p>
|
||||||
* Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426).
|
* 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 {
|
public class VCardParser_V30 implements VCardParser {
|
||||||
private static final String LOG_TAG = "VCardParser_V30";
|
/* package */ static final Set<String> sKnownPropertyNameSet =
|
||||||
|
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
|
||||||
private static final HashSet<String> sAcceptablePropsWithParam = new HashSet<String>(
|
|
||||||
Arrays.asList(
|
|
||||||
"BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
"BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
|
||||||
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
|
||||||
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
|
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
|
||||||
"NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
|
"NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
|
||||||
"SORT-STRING", "CATEGORIES", "PRODID")); // 3.0
|
"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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when the caller wants the parser to be strict about the input.
|
* <p>
|
||||||
* Currently this is only for testing.
|
* 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() {
|
public VCardParser_V30() {
|
||||||
super();
|
mVCardParserImpl = new VCardParserImpl_V30();
|
||||||
mStrictParsing = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public VCardParser_V30(int vcardType) {
|
||||||
* @param strictParsing when true, this object throws VCardException when the vcard is not
|
mVCardParserImpl = new VCardParserImpl_V30(vcardType);
|
||||||
* 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 parseMode) {
|
public void parse(InputStream is, VCardInterpreter interepreter)
|
||||||
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)
|
|
||||||
throws IOException, VCardException {
|
throws IOException, VCardException {
|
||||||
// TODO: vCard 3.0 supports group.
|
mVCardParserImpl.parse(is, interepreter);
|
||||||
super.readEndVCard(useCache, allowGarbage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void cancel() {
|
||||||
* vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
|
mVCardParserImpl.cancel();
|
||||||
*/
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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;
|
package android.pim.vcard;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class which tries to detects the source of the vCard from its properties.
|
* <p>
|
||||||
* Currently this implementation is very premature.
|
* The class which tries to detects the source of a vCard file from its contents.
|
||||||
* @hide
|
* </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 {
|
public class VCardSourceDetector implements VCardInterpreter {
|
||||||
|
private static final String LOG_TAG = "VCardSourceDetector";
|
||||||
|
|
||||||
private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
|
private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
|
||||||
"X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
|
"X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
|
||||||
"X-ABADR", "X-ABUID"));
|
"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-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
|
||||||
"X-SD-DESCRIPTION"));
|
"X-SD-DESCRIPTION"));
|
||||||
private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
|
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.
|
// Some mobile phones (like FOMA) tells us the charset of the data.
|
||||||
private boolean mNeedParseSpecifiedCharset;
|
private boolean mNeedToParseCharset;
|
||||||
private String mSpecifiedCharset;
|
private String mSpecifiedCharset;
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
@@ -58,7 +94,8 @@ public class VCardSourceDetector implements VCardInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startProperty() {
|
public void startProperty() {
|
||||||
mNeedParseSpecifiedCharset = false;
|
mNeedToParseCharset = false;
|
||||||
|
mNeedToParseVersion = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endProperty() {
|
public void endProperty() {
|
||||||
@@ -71,22 +108,26 @@ public class VCardSourceDetector implements VCardInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void propertyName(String name) {
|
public void propertyName(String name) {
|
||||||
if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
|
if (name.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION)) {
|
||||||
mType = VCardConfig.PARSE_TYPE_FOMA;
|
mNeedToParseVersion = true;
|
||||||
mNeedParseSpecifiedCharset = 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;
|
return;
|
||||||
}
|
}
|
||||||
if (mType != VCardConfig.PARSE_TYPE_UNKNOWN) {
|
if (mParseType != PARSE_TYPE_UNKNOWN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
|
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)) {
|
} 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)) {
|
} 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)) {
|
} 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) {
|
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);
|
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).
|
* This method must be called after parsing target file(s).
|
||||||
|
* </p>
|
||||||
* @return Charset String. Null is returned if guessing the source fails.
|
* @return Charset String. Null is returned if guessing the source fails.
|
||||||
*/
|
*/
|
||||||
public String getEstimatedCharset() {
|
public String getEstimatedCharset() {
|
||||||
if (mSpecifiedCharset != null) {
|
if (TextUtils.isEmpty(mSpecifiedCharset)) {
|
||||||
return mSpecifiedCharset;
|
return mSpecifiedCharset;
|
||||||
}
|
}
|
||||||
switch (mType) {
|
switch (mParseType) {
|
||||||
case VCardConfig.PARSE_TYPE_WINDOWS_MOBILE_JP:
|
case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
|
||||||
case VCardConfig.PARSE_TYPE_FOMA:
|
case PARSE_TYPE_DOCOMO_TORELATE_NEST:
|
||||||
case VCardConfig.PARSE_TYPE_MOBILE_PHONE_JP:
|
case PARSE_TYPE_MOBILE_PHONE_JP:
|
||||||
return "SHIFT_JIS";
|
return "SHIFT_JIS";
|
||||||
case VCardConfig.PARSE_TYPE_APPLE:
|
case PARSE_TYPE_APPLE:
|
||||||
return "UTF-8";
|
return "UTF-8";
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -16,13 +16,21 @@
|
|||||||
package android.pim.vcard;
|
package android.pim.vcard;
|
||||||
|
|
||||||
import android.content.ContentProviderOperation;
|
import android.content.ContentProviderOperation;
|
||||||
|
import android.pim.vcard.exception.VCardException;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
import android.provider.ContactsContract.CommonDataKinds.Im;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||||
import android.provider.ContactsContract.Data;
|
import android.provider.ContactsContract.Data;
|
||||||
import android.telephony.PhoneNumberUtils;
|
import android.telephony.PhoneNumberUtils;
|
||||||
import android.text.TextUtils;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -36,6 +44,8 @@ import java.util.Set;
|
|||||||
* Utilities for VCard handling codes.
|
* Utilities for VCard handling codes.
|
||||||
*/
|
*/
|
||||||
public class VCardUtils {
|
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
|
// 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
|
// converted to two parameter Strings. These only contain some minor fields valid in both
|
||||||
// vCard and current (as of 2009-08-07) Contacts structure.
|
// vCard and current (as of 2009-08-07) Contacts structure.
|
||||||
@@ -185,8 +195,7 @@ public class VCardUtils {
|
|||||||
// For backward compatibility.
|
// For backward compatibility.
|
||||||
// Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
|
// 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.
|
// To support mobile type at that time, this custom label had been used.
|
||||||
return (android.provider.Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME.equals(label)
|
return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label));
|
||||||
|| sMobilePhoneLabelSet.contains(label));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) {
|
public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) {
|
||||||
@@ -240,10 +249,13 @@ public class VCardUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <p>
|
||||||
* Inserts postal data into the builder object.
|
* Inserts postal data into the builder object.
|
||||||
*
|
* </p>
|
||||||
|
* <p>
|
||||||
* Note that the data structure of ContactsContract is different from that defined in vCard.
|
* Note that the data structure of ContactsContract is different from that defined in vCard.
|
||||||
* So some conversion may be performed in this method.
|
* So some conversion may be performed in this method.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
|
public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
|
||||||
final ContentProviderOperation.Builder builder,
|
final ContentProviderOperation.Builder builder,
|
||||||
@@ -319,18 +331,33 @@ public class VCardUtils {
|
|||||||
return builder.toString();
|
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,
|
public static List<String> constructListFromValue(final String value,
|
||||||
final boolean isV30) {
|
final int vcardType) {
|
||||||
final List<String> list = new ArrayList<String>();
|
final List<String> list = new ArrayList<String>();
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
int length = value.length();
|
final int length = value.length();
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
char ch = value.charAt(i);
|
char ch = value.charAt(i);
|
||||||
if (ch == '\\' && i < length - 1) {
|
if (ch == '\\' && i < length - 1) {
|
||||||
char nextCh = value.charAt(i + 1);
|
char nextCh = value.charAt(i + 1);
|
||||||
final String unescapedString =
|
final String unescapedString;
|
||||||
(isV30 ? VCardParser_V30.unescapeCharacter(nextCh) :
|
if (VCardConfig.isVersion40(vcardType)) {
|
||||||
VCardParser_V21.unescapeCharacter(nextCh));
|
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) {
|
if (unescapedString != null) {
|
||||||
builder.append(unescapedString);
|
builder.append(unescapedString);
|
||||||
i++;
|
i++;
|
||||||
@@ -371,9 +398,13 @@ public class VCardUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <p>
|
||||||
* This is useful when checking the string should be encoded into quoted-printable
|
* This is useful when checking the string should be encoded into quoted-printable
|
||||||
* or not, which is required by vCard 2.1.
|
* or not, which is required by vCard 2.1.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
* See the definition of "7bit" in vCard 2.1 spec for more information.
|
* See the definition of "7bit" in vCard 2.1 spec for more information.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) {
|
public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
@@ -407,13 +438,16 @@ public class VCardUtils {
|
|||||||
new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
|
new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <p>
|
||||||
* This is useful since vCard 3.0 often requires the ("X-") properties and groups
|
* This is useful since vCard 3.0 often requires the ("X-") properties and groups
|
||||||
* should contain only alphabets, digits, and hyphen.
|
* should contain only alphabets, digits, and hyphen.
|
||||||
*
|
* </p>
|
||||||
|
* <p>
|
||||||
* Note: It is already known some devices (wrongly) outputs properties with characters
|
* 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
|
* 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
|
* 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) {
|
public static boolean containsOnlyAlphaDigitHyphen(final String...values) {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
@@ -451,14 +485,39 @@ public class VCardUtils {
|
|||||||
return true;
|
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.
|
* Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
|
||||||
* </P>
|
* </p>
|
||||||
* <P>
|
* <p>
|
||||||
* vCard 2.1 specifies:<BR />
|
* vCard 2.1 specifies:<br />
|
||||||
* word = <any printable 7bit us-ascii except []=:., >
|
* word = <any printable 7bit us-ascii except []=:., >
|
||||||
* </P>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static boolean isV21Word(final String value) {
|
public static boolean isV21Word(final String value) {
|
||||||
if (TextUtils.isEmpty(value)) {
|
if (TextUtils.isEmpty(value)) {
|
||||||
@@ -477,6 +536,14 @@ public class VCardUtils {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int[] sEscapeIndicatorsV30 = new int[]{
|
||||||
|
':', ';', ',', ' '
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int[] sEscapeIndicatorsV40 = new int[]{
|
||||||
|
';', ':'
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <P>
|
||||||
* Returns String available as parameter value in vCard 3.0.
|
* 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.
|
* This method checks whether the given String can be used without quotes.
|
||||||
* </P>
|
* </P>
|
||||||
* <P>
|
* <P>
|
||||||
* Note: We remove DQUOTE silently for now.
|
* Note: We remove DQUOTE inside the given value silently for now.
|
||||||
* </P>
|
* </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)) {
|
if (TextUtils.isEmpty(value)) {
|
||||||
value = "";
|
value = "";
|
||||||
}
|
}
|
||||||
@@ -506,12 +581,19 @@ public class VCardUtils {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
builder.appendCodePoint(codePoint);
|
builder.appendCodePoint(codePoint);
|
||||||
if (codePoint == ':' || codePoint == ',' || codePoint == ' ') {
|
for (int indicator : escapeIndicators) {
|
||||||
needQuote = true;
|
if (codePoint == indicator) {
|
||||||
|
needQuote = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final String result = builder.toString();
|
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) {
|
public static String toHalfWidthString(final String orgString) {
|
||||||
@@ -577,6 +659,138 @@ public class VCardUtils {
|
|||||||
return true;
|
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() {
|
private VCardUtils() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package android.pim.vcard.exception;
|
package android.pim.vcard.exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package android.pim.vcard.exception;
|
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;
|
package android.pim.vcard;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
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.Email;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
import android.provider.ContactsContract.CommonDataKinds.Event;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
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.StructuredPostal;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||||
|
|
||||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,7 +57,6 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void testStructuredNameBasic(int vcardType) {
|
private void testStructuredNameBasic(int vcardType) {
|
||||||
final boolean isV30 = VCardConfig.isV30(vcardType);
|
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
.put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
|
||||||
@@ -64,28 +64,15 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
.put(StructuredName.DISPLAY_NAME, "DISPLAY NAME");
|
||||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
|
||||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
|
|
||||||
|
|
||||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("N",
|
.addExpectedNodeWithOrder("N",
|
||||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||||
+ "AppropriatePrefix;AppropriateSuffix",
|
+ "AppropriatePrefix;AppropriateSuffix",
|
||||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||||
.addExpectedNodeWithOrder("FN",
|
.addExpectedNodeWithOrder("FN", "DISPLAY NAME");
|
||||||
"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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStructuredNameBasicV21() {
|
public void testStructuredNameBasicV21() {
|
||||||
@@ -96,6 +83,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testStructuredNameBasic(V30);
|
testStructuredNameBasic(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testStructuredNameBasicV40() {
|
||||||
|
testStructuredNameBasic(V40);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that only "primary" StructuredName is emitted, so that our vCard file
|
* Test that only "primary" StructuredName is emitted, so that our vCard file
|
||||||
* will not confuse the external importer, assuming there may be some importer
|
* 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.
|
* Note that more than one "N", "FN", etc. properties are acceptable in vCard spec.
|
||||||
*/
|
*/
|
||||||
private void testStructuredNameUsePrimaryCommon(int vcardType) {
|
private void testStructuredNameUsePrimaryCommon(int vcardType) {
|
||||||
final boolean isV30 = (vcardType == V30);
|
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
final ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
||||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
||||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
|
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
|
||||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix1")
|
.put(StructuredName.PREFIX, "DoNotEmitPrefix1")
|
||||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
|
.put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
|
||||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1")
|
.put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplayName1");
|
||||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1")
|
|
||||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
|
|
||||||
|
|
||||||
// With "IS_PRIMARY=1". This is what we should use.
|
// With "IS_PRIMARY=1". This is what we should use.
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
@@ -123,41 +111,30 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
.put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
|
||||||
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
.put(StructuredName.PREFIX, "AppropriatePrefix")
|
||||||
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
.put(StructuredName.SUFFIX, "AppropriateSuffix")
|
||||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
|
.put(StructuredName.DISPLAY_NAME, "AppropriateDisplayName")
|
||||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
|
|
||||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
|
|
||||||
.put(StructuredName.IS_PRIMARY, 1);
|
.put(StructuredName.IS_PRIMARY, 1);
|
||||||
|
|
||||||
// With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
|
// 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)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
|
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
|
||||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
|
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
|
||||||
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
|
.put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
|
||||||
.put(StructuredName.PREFIX, "DoNotEmitPrefix2")
|
.put(StructuredName.PREFIX, "DoNotEmitPrefix2")
|
||||||
.put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
|
.put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
|
||||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2")
|
.put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplayName2")
|
||||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2")
|
|
||||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2")
|
|
||||||
.put(StructuredName.IS_PRIMARY, 1);
|
.put(StructuredName.IS_PRIMARY, 1);
|
||||||
|
|
||||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("N",
|
.addExpectedNodeWithOrder("N",
|
||||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||||
+ "AppropriatePrefix;AppropriateSuffix",
|
+ "AppropriatePrefix;AppropriateSuffix",
|
||||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
||||||
.addExpectedNodeWithOrder("FN",
|
.addExpectedNodeWithOrder("FN", "AppropriateDisplayName");
|
||||||
"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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStructuredNameUsePrimaryV21() {
|
public void testStructuredNameUsePrimaryV21() {
|
||||||
@@ -168,14 +145,152 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testStructuredNameUsePrimaryCommon(V30);
|
testStructuredNameUsePrimaryCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testStructuredNameUsePrimaryV40() {
|
||||||
|
testStructuredNameUsePrimaryCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that only "super primary" StructuredName is emitted.
|
* Tests that only "super primary" StructuredName is emitted.
|
||||||
* See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
|
* See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
|
||||||
*/
|
*/
|
||||||
private void testStructuredNameUseSuperPrimaryCommon(int vcardType) {
|
private void testStructuredNameUseSuperPrimaryCommon(int vcardType) {
|
||||||
final boolean isV30 = (vcardType == V30);
|
|
||||||
mVerifier.initForExportTest(vcardType);
|
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)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
.put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
|
||||||
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
.put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
|
||||||
@@ -221,32 +336,50 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3")
|
.put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3")
|
||||||
.put(StructuredName.IS_PRIMARY, 1);
|
.put(StructuredName.IS_PRIMARY, 1);
|
||||||
|
|
||||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
|
final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem();
|
||||||
.addExpectedNodeWithOrder("N",
|
if (VCardConfig.isVersion40(vcardType)) {
|
||||||
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
final ContentValues contentValues = new ContentValues();
|
||||||
+ "AppropriatePrefix;AppropriateSuffix",
|
contentValues.put("SORT-AS",
|
||||||
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName");
|
||||||
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
|
// vCard 4.0 (rev13) now uses SORT-AS parameter, which is not compatible with
|
||||||
.addExpectedNodeWithOrder("FN",
|
// either 2.1 nor 3.0.
|
||||||
"AppropriatePrefix AppropriateGivenName "
|
elem.addExpectedNodeWithOrder("N",
|
||||||
+ "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
|
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
|
+ "AppropriatePrefix;AppropriateSuffix",
|
||||||
.addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
|
Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
|
||||||
.addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
|
"AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"),
|
||||||
|
contentValues);
|
||||||
if (isV30) {
|
} else {
|
||||||
elem.addExpectedNode("SORT-STRING",
|
elem.addExpectedNodeWithOrder("N",
|
||||||
"AppropriatePhoneticGiven AppropriatePhoneticMiddle"
|
"AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
|
||||||
+ " AppropriatePhoneticFamily");
|
+ "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() {
|
public void testStructuredNameComplicatedV21() {
|
||||||
testStructuredNameUseSuperPrimaryCommon(V21);
|
testStructuredNameComplicatedCommon(V21);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStructuredNameUseSuperPrimaryV30() {
|
public void testStructuredNameComplicatedV30() {
|
||||||
testStructuredNameUseSuperPrimaryCommon(V30);
|
testStructuredNameComplicatedCommon(V30);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStructuredNameComplicatedV40() {
|
||||||
|
testStructuredNameComplicatedCommon(V40);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNickNameV30() {
|
public void testNickNameV30() {
|
||||||
@@ -258,6 +391,15 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
.addExpectedNodeWithOrder("NICKNAME", "Nicky");
|
.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) {
|
private void testPhoneBasicCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||||
@@ -275,6 +417,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPhoneBasicCommon(V30);
|
testPhoneBasicCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPhoneBasicV40() {
|
||||||
|
testPhoneBasicCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
public void testPhoneRefrainFormatting() {
|
public void testPhoneRefrainFormatting() {
|
||||||
mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING);
|
mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING);
|
||||||
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||||
@@ -367,6 +513,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPhoneVariousTypeSupport(V30);
|
testPhoneVariousTypeSupport(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPhoneVariousTypeSupportV40() {
|
||||||
|
testPhoneVariousTypeSupport(V40);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that "PREF"s are emitted appropriately.
|
* Tests that "PREF"s are emitted appropriately.
|
||||||
*/
|
*/
|
||||||
@@ -403,6 +553,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPhonePrefHandlingCommon(V30);
|
testPhonePrefHandlingCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPhonePrefHandlingV40() {
|
||||||
|
testPhonePrefHandlingCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testMiscPhoneTypeHandling(int vcardType) {
|
private void testMiscPhoneTypeHandling(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -438,7 +592,7 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
.put(Phone.TYPE, Phone.TYPE_CUSTOM)
|
||||||
.put(Phone.LABEL, "invalid");
|
.put(Phone.LABEL, "invalid");
|
||||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||||
if (VCardConfig.isV30(vcardType)) {
|
if (VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType)) {
|
||||||
// vCard 3.0 accepts "invalid". Also stop using toUpper()
|
// vCard 3.0 accepts "invalid". Also stop using toUpper()
|
||||||
elem.addExpectedNode("TEL", "1", new TypeSet("Modem"))
|
elem.addExpectedNode("TEL", "1", new TypeSet("Modem"))
|
||||||
.addExpectedNode("TEL", "2", new TypeSet("MSG"))
|
.addExpectedNode("TEL", "2", new TypeSet("MSG"))
|
||||||
@@ -468,6 +622,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testMiscPhoneTypeHandling(V30);
|
testMiscPhoneTypeHandling(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPhoneTypeHandlingV40() {
|
||||||
|
testMiscPhoneTypeHandling(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testEmailBasicCommon(int vcardType) {
|
private void testEmailBasicCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE)
|
||||||
@@ -484,6 +642,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testEmailBasicCommon(V30);
|
testEmailBasicCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEmailBasicV40() {
|
||||||
|
testEmailBasicCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testEmailVariousTypeSupportCommon(int vcardType) {
|
private void testEmailVariousTypeSupportCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -514,6 +676,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testEmailVariousTypeSupportCommon(V30);
|
testEmailVariousTypeSupportCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEmailVariousTypeSupportV40() {
|
||||||
|
testEmailVariousTypeSupportCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testEmailPrefHandlingCommon(int vcardType) {
|
private void testEmailPrefHandlingCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -538,6 +704,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testEmailPrefHandlingCommon(V30);
|
testEmailPrefHandlingCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEmailPrefHandlingV40() {
|
||||||
|
testEmailPrefHandlingCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testPostalAddressCommon(int vcardType) {
|
private void testPostalAddressCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
@@ -569,6 +739,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPostalAddressCommon(V30);
|
testPostalAddressCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPostalAddressV40() {
|
||||||
|
testPostalAddressCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testPostalAddressNonNeighborhood(int vcardType) {
|
private void testPostalAddressNonNeighborhood(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
@@ -586,6 +760,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPostalAddressNonNeighborhood(V30);
|
testPostalAddressNonNeighborhood(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPostalAddressNonNeighborhoodV40() {
|
||||||
|
testPostalAddressNonNeighborhood(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testPostalAddressNonCity(int vcardType) {
|
private void testPostalAddressNonCity(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
@@ -603,6 +781,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPostalAddressNonCity(V30);
|
testPostalAddressNonCity(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPostalAddressNonCityV40() {
|
||||||
|
testPostalAddressNonCity(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testPostalOnlyWithFormattedAddressCommon(int vcardType) {
|
private void testPostalOnlyWithFormattedAddressCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
@@ -623,6 +805,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPostalOnlyWithFormattedAddressCommon(V30);
|
testPostalOnlyWithFormattedAddressCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPostalOnlyWithFormattedAddressV40() {
|
||||||
|
testPostalOnlyWithFormattedAddressCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that the vCard composer honors formatted data when it is available
|
* Tests that the vCard composer honors formatted data when it is available
|
||||||
* even when it is partial.
|
* even when it is partial.
|
||||||
@@ -648,6 +834,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPostalWithBothStructuredAndFormattedCommon(V30);
|
testPostalWithBothStructuredAndFormattedCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPostalWithBothStructuredAndFormattedV40() {
|
||||||
|
testPostalWithBothStructuredAndFormattedCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testOrganizationCommon(int vcardType) {
|
private void testOrganizationCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -685,6 +875,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testOrganizationCommon(V30);
|
testOrganizationCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testOrganizationV40() {
|
||||||
|
testOrganizationCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testImVariousTypeSupportCommon(int vcardType) {
|
private void testImVariousTypeSupportCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -737,6 +931,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testImVariousTypeSupportCommon(V30);
|
testImVariousTypeSupportCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testImBasicV40() {
|
||||||
|
testImVariousTypeSupportCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testImPrefHandlingCommon(int vcardType) {
|
private void testImPrefHandlingCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -762,6 +960,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testImPrefHandlingCommon(V30);
|
testImPrefHandlingCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testImPrefHandlingV40() {
|
||||||
|
testImPrefHandlingCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testWebsiteCommon(int vcardType) {
|
private void testWebsiteCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -786,6 +988,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testWebsiteCommon(V30);
|
testWebsiteCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWebsiteV40() {
|
||||||
|
testWebsiteCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private String getAndroidPropValue(final String mimeType, String value, Integer type) {
|
private String getAndroidPropValue(final String mimeType, String value, Integer type) {
|
||||||
return getAndroidPropValue(mimeType, value, type, null);
|
return getAndroidPropValue(mimeType, value, type, null);
|
||||||
}
|
}
|
||||||
@@ -839,6 +1045,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testEventCommon(V30);
|
testEventCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEventV40() {
|
||||||
|
testEventCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testNoteCommon(int vcardType) {
|
private void testNoteCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
@@ -860,8 +1070,13 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testNoteCommon(V30);
|
testNoteCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testNoteV40() {
|
||||||
|
testNoteCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testPhotoCommon(int vcardType) {
|
private void testPhotoCommon(int vcardType) {
|
||||||
final boolean isV30 = vcardType == V30;
|
final boolean useB =
|
||||||
|
(VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType));
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
@@ -870,7 +1085,7 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
.put(Photo.PHOTO, sPhotoByteArray);
|
.put(Photo.PHOTO, sPhotoByteArray);
|
||||||
|
|
||||||
ContentValues contentValuesForPhoto = new ContentValues();
|
ContentValues contentValuesForPhoto = new ContentValues();
|
||||||
contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64"));
|
contentValuesForPhoto.put("ENCODING", (useB ? "b" : "BASE64"));
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNode("FN", "PhotoTest")
|
.addExpectedNode("FN", "PhotoTest")
|
||||||
.addExpectedNode("N", "PhotoTest;;;;",
|
.addExpectedNode("N", "PhotoTest;;;;",
|
||||||
@@ -887,6 +1102,10 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
testPhotoCommon(V30);
|
testPhotoCommon(V30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPhotoV40() {
|
||||||
|
testPhotoCommon(V40);
|
||||||
|
}
|
||||||
|
|
||||||
private void testRelationCommon(int vcardType) {
|
private void testRelationCommon(int vcardType) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType);
|
||||||
mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE)
|
mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE)
|
||||||
@@ -957,18 +1176,22 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.IS_PRIMARY, 1); // Empty name. Should be ignored.
|
.put(StructuredName.IS_PRIMARY, 1); // Empty name. Should be ignored.
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
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)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.IS_PRIMARY, 1)
|
.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)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.IS_PRIMARY, 1)
|
.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)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "family4");
|
.put(StructuredName.FAMILY_NAME, "family4")
|
||||||
|
.put(StructuredName.DISPLAY_NAME, "display");
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNode("N", Arrays.asList("family2", "", "", "", ""))
|
.addExpectedNode("N", Arrays.asList("family2", "", "", "", ""))
|
||||||
.addExpectedNode("FN", "family2");
|
.addExpectedNode("FN", "display");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPickUpNonEmptyContentValuesV21() {
|
public void testPickUpNonEmptyContentValuesV21() {
|
||||||
@@ -978,4 +1201,23 @@ public class VCardExporterTests extends VCardTestsBase {
|
|||||||
public void testPickUpNonEmptyContentValuesV30() {
|
public void testPickUpNonEmptyContentValuesV30() {
|
||||||
testPickUpNonEmptyContentValuesCommon(V30);
|
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;
|
package android.pim.vcard;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.pim.vcard.VCardConfig;
|
import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
|
||||||
import android.provider.ContactsContract.Data;
|
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Event;
|
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.Note;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Organization;
|
import android.provider.ContactsContract.CommonDataKinds.Organization;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
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.StructuredName;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||||
|
import android.provider.ContactsContract.Data;
|
||||||
import com.android.frameworks.coretests.R;
|
|
||||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -405,12 +406,12 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
|
|
||||||
public void testV21SimpleCase1_Parsing() {
|
public void testV21SimpleCase1_Parsing() {
|
||||||
mVerifier.initForImportTest(V21, R.raw.v21_simple_1);
|
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", ""));
|
.addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testV21SimpleCase1_Type_Generic() {
|
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()
|
mVerifier.addContentValuesVerifierElem()
|
||||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "Ando")
|
.put(StructuredName.FAMILY_NAME, "Ando")
|
||||||
@@ -419,7 +420,7 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testV21SimpleCase1_Type_Japanese() {
|
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()
|
mVerifier.addContentValuesVerifierElem()
|
||||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "Ando")
|
.put(StructuredName.FAMILY_NAME, "Ando")
|
||||||
@@ -431,7 +432,7 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testV21SimpleCase2() {
|
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()
|
mVerifier.addContentValuesVerifierElem()
|
||||||
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.DISPLAY_NAME, "Ando Roid");
|
.put(StructuredName.DISPLAY_NAME, "Ando Roid");
|
||||||
@@ -454,7 +455,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
public void testV21BackslashCase_Parsing() {
|
public void testV21BackslashCase_Parsing() {
|
||||||
mVerifier.initForImportTest(V21, R.raw.v21_backslash);
|
mVerifier.initForImportTest(V21, R.raw.v21_backslash);
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
|
||||||
.addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;",
|
.addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;",
|
||||||
Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""))
|
Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""))
|
||||||
.addExpectedNodeWithOrder("FN", "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() {
|
public void testV21ComplicatedCase_Parsing() {
|
||||||
mVerifier.initForImportTest(V21, R.raw.v21_complicated);
|
mVerifier.initForImportTest(V21, R.raw.v21_complicated);
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
|
||||||
.addExpectedNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao",
|
.addExpectedNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao",
|
||||||
Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao"))
|
Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao"))
|
||||||
.addExpectedNodeWithOrder("FN", "Joe Due")
|
.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"))
|
Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper"))
|
||||||
.addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!")
|
.addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!")
|
||||||
.addExpectedNodeWithOrder("TITLE", "Shrimp Man")
|
.addExpectedNodeWithOrder("TITLE", "Shrimp Man")
|
||||||
@@ -583,7 +583,8 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
.addExpectedNodeWithOrder("EMAIL", "forrestgump@walladalla.com",
|
.addExpectedNodeWithOrder("EMAIL", "forrestgump@walladalla.com",
|
||||||
new TypeSet("PREF", "INTERNET"))
|
new TypeSet("PREF", "INTERNET"))
|
||||||
.addExpectedNodeWithOrder("EMAIL", "cell@example.com", new TypeSet("CELL"))
|
.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",
|
.addExpectedNodeWithOrder("NOTE",
|
||||||
"Now's the time for all folk to come to the aid of their country.",
|
"Now's the time for all folk to come to the aid of their country.",
|
||||||
null, null, mContentValuesForQP, null, null)
|
null, null, mContentValuesForQP, null, null)
|
||||||
@@ -677,12 +678,24 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
.put(Website.TYPE, Website.TYPE_HOMEPAGE);
|
.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() {
|
public void testV30Simple_Parsing() {
|
||||||
mVerifier.initForImportTest(V30, R.raw.v30_simple);
|
mVerifier.initForImportTest(V30, R.raw.v30_simple);
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "3.0")
|
|
||||||
.addExpectedNodeWithOrder("FN", "And Roid")
|
.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",
|
.addExpectedNodeWithOrder("ORG", "Open;Handset; Alliance",
|
||||||
Arrays.asList("Open", "Handset", " Alliance"))
|
Arrays.asList("Open", "Handset", " Alliance"))
|
||||||
.addExpectedNodeWithOrder("SORT-STRING", "android")
|
.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",
|
// Though Japanese careers append ";;;;" at the end of the value of "SOUND",
|
||||||
// vCard 2.1/3.0 specification does not allow multiple values.
|
// vCard 2.1/3.0 specification does not allow multiple values.
|
||||||
// Do not need to handle it as multiple values.
|
// Do not need to handle it as multiple values.
|
||||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_1);
|
||||||
R.raw.v21_japanese_1);
|
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1", null, null, null, null, null)
|
|
||||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
|
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
|
||||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
|
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
|
||||||
null, mContentValuesForSJis, null, null)
|
null, mContentValuesForSJis, null, null)
|
||||||
@@ -752,37 +763,35 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies vCard with Japanese can be parsed correctly with
|
* 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() {
|
public void testV21Japanese1_Type_Generic_Utf8() {
|
||||||
testV21Japanese1Common(
|
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
|
* 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() {
|
public void testV21Japanese1_Type_Japanese_Sjis() {
|
||||||
testV21Japanese1Common(
|
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
|
* 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.
|
* since vCard 2.1 specifies the charset of each line if it contains non-Ascii.
|
||||||
*/
|
*/
|
||||||
public void testV21Japanese1_Type_Japanese_Utf8() {
|
public void testV21Japanese1_Type_Japanese_Utf8() {
|
||||||
testV21Japanese1Common(
|
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() {
|
public void testV21Japanese2_Parsing() {
|
||||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_2);
|
||||||
R.raw.v21_japanese_2);
|
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
|
||||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
|
.addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
|
||||||
Arrays.asList("\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() {
|
public void testV21MultipleEntryCase_Parse() {
|
||||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry);
|
||||||
R.raw.v21_multiple_entry);
|
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
|
||||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
|
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
|
||||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
|
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
|
||||||
null, mContentValuesForSJis, null, null)
|
null, mContentValuesForSJis, null, null)
|
||||||
@@ -854,7 +861,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
.addExpectedNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL"))
|
.addExpectedNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL"))
|
||||||
.addExpectedNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME"));
|
.addExpectedNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME"));
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
|
||||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
|
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
|
||||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
|
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
|
||||||
null, mContentValuesForSJis, null, null)
|
null, mContentValuesForSJis, null, null)
|
||||||
@@ -867,7 +873,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
.addExpectedNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY"))
|
.addExpectedNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY"))
|
||||||
.addExpectedNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL"));
|
.addExpectedNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL"));
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
|
||||||
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
|
.addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
|
||||||
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
|
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
|
||||||
null, mContentValuesForSJis, null, null)
|
null, mContentValuesForSJis, null, null)
|
||||||
@@ -882,8 +887,7 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testV21MultipleEntryCase() {
|
public void testV21MultipleEntryCase() {
|
||||||
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
|
mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry);
|
||||||
R.raw.v21_multiple_entry);
|
|
||||||
ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
||||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033")
|
.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033")
|
||||||
@@ -957,7 +961,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
ContentValues contentValuesForValue = new ContentValues();
|
ContentValues contentValuesForValue = new ContentValues();
|
||||||
contentValuesForValue.put("VALUE", "DATE");
|
contentValuesForValue.put("VALUE", "DATE");
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "2.1")
|
|
||||||
.addExpectedNodeWithOrder("N", Arrays.asList("Example", "", "", "", ""))
|
.addExpectedNodeWithOrder("N", Arrays.asList("Example", "", "", "", ""))
|
||||||
.addExpectedNodeWithOrder("FN", "Example")
|
.addExpectedNodeWithOrder("FN", "Example")
|
||||||
.addExpectedNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue)
|
.addExpectedNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue)
|
||||||
@@ -974,6 +977,9 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "Example")
|
.put(StructuredName.FAMILY_NAME, "Example")
|
||||||
.put(StructuredName.DISPLAY_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() {
|
public void testTolerateInvalidCommentLikeLineV21() {
|
||||||
@@ -988,16 +994,15 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testPagerV30_Parse() {
|
public void testPagerV30_Parse() {
|
||||||
mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
|
mVerifier.initForImportTest(V30, R.raw.v30_pager);
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "3.0")
|
|
||||||
.addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
|
.addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
|
||||||
.addExpectedNodeWithOrder("TEL", "6101231234@pagersample.com",
|
.addExpectedNodeWithOrder("TEL", "6101231234@pagersample.com",
|
||||||
new TypeSet("WORK", "MSG", "PAGER"));
|
new TypeSet("WORK", "MSG", "PAGER"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPagerV30() {
|
public void testPagerV30() {
|
||||||
mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
|
mVerifier.initForImportTest(V30, R.raw.v30_pager);
|
||||||
ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
|
||||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "F")
|
.put(StructuredName.FAMILY_NAME, "F")
|
||||||
@@ -1012,7 +1017,6 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
public void testMultiBytePropV30_Parse() {
|
public void testMultiBytePropV30_Parse() {
|
||||||
mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
|
mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNodeWithOrder("VERSION", "3.0")
|
|
||||||
.addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
|
.addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
|
||||||
.addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39"));
|
.addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39"));
|
||||||
}
|
}
|
||||||
@@ -1030,4 +1034,72 @@ public class VCardImporterTests extends VCardTestsBase {
|
|||||||
.put(Phone.LABEL, "\u8D39")
|
.put(Phone.LABEL, "\u8D39")
|
||||||
.put(Phone.NUMBER, "1");
|
.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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package android.pim.vcard;
|
package android.pim.vcard;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
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.Nickname;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Note;
|
import android.provider.ContactsContract.CommonDataKinds.Note;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||||
|
|
||||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class VCardJapanizationTests extends VCardTestsBase {
|
public class VCardJapanizationTests extends VCardTestsBase {
|
||||||
@@ -39,7 +36,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
.put(StructuredName.PREFIX, "Dr.")
|
.put(StructuredName.PREFIX, "Dr.")
|
||||||
.put(StructuredName.SUFFIX, "Ph.D");
|
.put(StructuredName.SUFFIX, "Ph.D");
|
||||||
ContentValues contentValues =
|
ContentValues contentValues =
|
||||||
(VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8);
|
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null);
|
||||||
mVerifier.addPropertyNodesVerifierElem()
|
mVerifier.addPropertyNodesVerifierElem()
|
||||||
.addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
|
.addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
|
||||||
contentValues)
|
contentValues)
|
||||||
@@ -50,15 +47,15 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testNameUtf8V21() {
|
public void testNameUtf8V21() {
|
||||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
|
testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNameUtf8V30() {
|
public void testNameUtf8V30() {
|
||||||
testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
|
testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNameShiftJis() {
|
public void testNameShiftJis() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
.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.
|
* DoCoMo phones require all name elements should be in "family name" field.
|
||||||
*/
|
*/
|
||||||
public void testNameDoCoMo() {
|
public void testNameDoCoMo() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
.put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
|
||||||
@@ -105,8 +102,8 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
.addExpectedNode("X-DCM-HMN-MODE", "");
|
.addExpectedNode("X-DCM-HMN-MODE", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testPhoneticNameCommon(int vcardType) {
|
private void testPhoneticNameCommon(int vcardType, String charset) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType, charset);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
.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");
|
.put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
|
||||||
|
|
||||||
final ContentValues contentValues =
|
final ContentValues contentValues =
|
||||||
(VCardConfig.usesShiftJis(vcardType) ?
|
("SHIFT_JIS".equalsIgnoreCase(charset) ?
|
||||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis :
|
||||||
mContentValuesForQPAndSJis) :
|
mContentValuesForSJis) :
|
||||||
(VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8));
|
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null));
|
||||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||||
elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060",
|
elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060",
|
||||||
contentValues)
|
contentValues)
|
||||||
@@ -126,7 +123,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
contentValues)
|
contentValues)
|
||||||
.addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046",
|
.addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046",
|
||||||
contentValues);
|
contentValues);
|
||||||
if (VCardConfig.isV30(vcardType)) {
|
if (!VCardConfig.isVersion21(vcardType)) {
|
||||||
elem.addExpectedNode("SORT-STRING",
|
elem.addExpectedNode("SORT-STRING",
|
||||||
"\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046",
|
"\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046",
|
||||||
contentValues);
|
contentValues);
|
||||||
@@ -142,23 +139,23 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testPhoneticNameForJapaneseV21Utf8() {
|
public void testPhoneticNameForJapaneseV21Utf8() {
|
||||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
|
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPhoneticNameForJapaneseV21Sjis() {
|
public void testPhoneticNameForJapaneseV21Sjis() {
|
||||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
|
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPhoneticNameForJapaneseV30Utf8() {
|
public void testPhoneticNameForJapaneseV30Utf8() {
|
||||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
|
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPhoneticNameForJapaneseV30SJis() {
|
public void testPhoneticNameForJapaneseV30SJis() {
|
||||||
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
|
testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPhoneticNameForMobileV21_1() {
|
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();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
||||||
@@ -182,7 +179,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testPhoneticNameForMobileV21_2() {
|
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();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
|
.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");
|
.put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testPostalAddressWithJapaneseCommon(int vcardType) {
|
private void testPostalAddressWithJapaneseCommon(int vcardType, String charset) {
|
||||||
mVerifier.initForExportTest(vcardType);
|
mVerifier.initForExportTest(vcardType, charset);
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
|
.put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
|
||||||
@@ -214,11 +211,11 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||||
.put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A");
|
.put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A");
|
||||||
|
|
||||||
ContentValues contentValues = (VCardConfig.usesShiftJis(vcardType) ?
|
ContentValues contentValues = ("UTF-8".equalsIgnoreCase(charset) ?
|
||||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis :
|
||||||
mContentValuesForQPAndSJis) :
|
mContentValuesForSJis) :
|
||||||
(VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 :
|
(VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 :
|
||||||
mContentValuesForQPAndUtf8));
|
mContentValuesForUtf8));
|
||||||
|
|
||||||
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
|
||||||
// LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is
|
// 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);
|
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
|
||||||
}
|
}
|
||||||
public void testPostalAddresswithJapaneseV21() {
|
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
|
* Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM
|
||||||
*/
|
*/
|
||||||
public void testPostalAdrressForDoCoMo_1() {
|
public void testPostalAdrressForDoCoMo_1() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
|
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
|
||||||
@@ -276,7 +273,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testPostalAdrressForDoCoMo_2() {
|
public void testPostalAdrressForDoCoMo_2() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
|
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
|
||||||
@@ -301,7 +298,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testPostalAdrressForDoCoMo_3() {
|
public void testPostalAdrressForDoCoMo_3() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||||
@@ -329,7 +326,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
* Verifies the vCard exporter tolerates null TYPE.
|
* Verifies the vCard exporter tolerates null TYPE.
|
||||||
*/
|
*/
|
||||||
public void testPostalAdrressForDoCoMo_4() {
|
public void testPostalAdrressForDoCoMo_4() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
|
||||||
.put(StructuredPostal.POBOX, "1");
|
.put(StructuredPostal.POBOX, "1");
|
||||||
@@ -371,15 +368,15 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testJapanesePhoneNumberV21_1() {
|
public void testJapanesePhoneNumberV21_1() {
|
||||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
|
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testJapanesePhoneNumberV30() {
|
public void testJapanesePhoneNumberV30() {
|
||||||
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
|
testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testJapanesePhoneNumberDoCoMo() {
|
public void testJapanesePhoneNumberDoCoMo() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
|
||||||
.put(Phone.NUMBER, "0312341234")
|
.put(Phone.NUMBER, "0312341234")
|
||||||
@@ -399,7 +396,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testNoteDoCoMo() {
|
public void testNoteDoCoMo() {
|
||||||
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
|
mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
|
||||||
ContactEntry entry = mVerifier.addInputEntry();
|
ContactEntry entry = mVerifier.addInputEntry();
|
||||||
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
entry.addContentValues(Note.CONTENT_ITEM_TYPE)
|
||||||
.put(Note.NOTE, "note1");
|
.put(Note.NOTE, "note1");
|
||||||
@@ -421,7 +418,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAndroidCustomV21() {
|
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)
|
mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
|
||||||
.put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC");
|
.put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC");
|
||||||
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package android.pim.vcard;
|
package android.pim.vcard;
|
||||||
|
|
||||||
import android.pim.vcard.VCardUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
@@ -85,31 +84,35 @@ public class VCardUtilsTests extends TestCase {
|
|||||||
|
|
||||||
public void testToStringAvailableAsV30ParamValue() {
|
public void testToStringAvailableAsV30ParamValue() {
|
||||||
// Smoke tests.
|
// Smoke tests.
|
||||||
assertEquals("HOME", VCardUtils.toStringAvailableAsV30ParameValue("HOME"));
|
assertEquals("HOME", VCardUtils.toStringAsV30ParamValue("HOME"));
|
||||||
assertEquals("TEL", VCardUtils.toStringAvailableAsV30ParameValue("TEL"));
|
assertEquals("TEL", VCardUtils.toStringAsV30ParamValue("TEL"));
|
||||||
assertEquals("PAGER", VCardUtils.toStringAvailableAsV30ParameValue("PAGER"));
|
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
|
// non-Ascii must be allowed
|
||||||
assertEquals("\u4E8B\u52D9\u6240",
|
assertEquals("\u4E8B\u52D9\u6240",
|
||||||
VCardUtils.toStringAvailableAsV30ParameValue("\u4E8B\u52D9\u6240"));
|
VCardUtils.toStringAsV30ParamValue("\u4E8B\u52D9\u6240"));
|
||||||
// Reported as bug report.
|
// Reported as bug report.
|
||||||
assertEquals("\u8D39", VCardUtils.toStringAvailableAsV30ParameValue("\u8D39"));
|
assertEquals("\u8D39", VCardUtils.toStringAsV30ParamValue("\u8D39"));
|
||||||
assertEquals("\"comma,separated\"",
|
assertEquals("\"comma,separated\"",
|
||||||
VCardUtils.toStringAvailableAsV30ParameValue("comma,separated"));
|
VCardUtils.toStringAsV30ParamValue("comma,separated"));
|
||||||
assertEquals("\"colon:aware\"",
|
assertEquals("\"colon:aware\"",
|
||||||
VCardUtils.toStringAvailableAsV30ParameValue("colon:aware"));
|
VCardUtils.toStringAsV30ParamValue("colon:aware"));
|
||||||
// CTL characters.
|
// CTL characters.
|
||||||
assertEquals("CTLExample",
|
assertEquals("CTLExample",
|
||||||
VCardUtils.toStringAvailableAsV30ParameValue("CTL\u0001Example"));
|
VCardUtils.toStringAsV30ParamValue("CTL\u0001Example"));
|
||||||
|
assertTrue(TextUtils.isEmpty(
|
||||||
|
VCardUtils.toStringAsV30ParamValue("\u0001\u0002\u0003")));
|
||||||
// DQUOTE must be removed.
|
// DQUOTE must be removed.
|
||||||
assertEquals("quoted",
|
assertEquals("quoted",
|
||||||
VCardUtils.toStringAvailableAsV30ParameValue("\"quoted\""));
|
VCardUtils.toStringAsV30ParamValue("\"quoted\""));
|
||||||
// DQUOTE must be removed basically, but we should detect a space, which
|
// DQUOTE must be removed basically, but we should detect a space, which
|
||||||
// require us to use DQUOTE again.
|
// require us to use DQUOTE again.
|
||||||
// Right-side has one more illegal dquote to test quote-handle code thoroughly.
|
// Right-side has one more illegal dquote to test quote-handle code thoroughly.
|
||||||
assertEquals("\"Already quoted\"",
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package android.pim.vcard.test_utils;
|
||||||
package android.pim.vcard;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ import android.content.ContentValues;
|
|||||||
* ContentValues-like class which enables users to chain put() methods and restricts
|
* ContentValues-like class which enables users to chain put() methods and restricts
|
||||||
* the other methods.
|
* the other methods.
|
||||||
*/
|
*/
|
||||||
/* package */ class ContentValuesBuilder {
|
public class ContentValuesBuilder {
|
||||||
private final ContentValues mContentValues;
|
private final ContentValues mContentValues;
|
||||||
|
|
||||||
public ContentValuesBuilder(final ContentValues contentValues) {
|
public ContentValuesBuilder(final ContentValues contentValues) {
|
||||||
@@ -34,6 +33,7 @@ import android.content.ContentValues;
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
public ContentValuesBuilder put(String key, Byte value) {
|
public ContentValuesBuilder put(String key, Byte value) {
|
||||||
mContentValues.put(key, value);
|
mContentValues.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
@@ -42,13 +42,14 @@ import android.content.ContentValues;
|
|||||||
public ContentValuesBuilder put(String key, Short value) {
|
public ContentValuesBuilder put(String key, Short value) {
|
||||||
mContentValues.put(key, value);
|
mContentValues.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public ContentValuesBuilder put(String key, Integer value) {
|
public ContentValuesBuilder put(String key, Integer value) {
|
||||||
mContentValues.put(key, value);
|
mContentValues.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
public ContentValuesBuilder put(String key, Long value) {
|
public ContentValuesBuilder put(String key, Long value) {
|
||||||
mContentValues.put(key, value);
|
mContentValues.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
@@ -67,7 +68,7 @@ import android.content.ContentValues;
|
|||||||
public ContentValuesBuilder put(String key, Boolean value) {
|
public ContentValuesBuilder put(String key, Boolean value) {
|
||||||
mContentValues.put(key, value);
|
mContentValues.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public ContentValuesBuilder put(String key, byte[] value) {
|
public ContentValuesBuilder put(String key, byte[] value) {
|
||||||
mContentValues.put(key, 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.pim.vcard.VCardConfig;
|
|
||||||
import android.pim.vcard.VCardEntry;
|
import android.pim.vcard.VCardEntry;
|
||||||
import android.pim.vcard.VCardEntryCommitter;
|
import android.pim.vcard.VCardEntryCommitter;
|
||||||
import android.pim.vcard.VCardEntryConstructor;
|
import android.pim.vcard.VCardEntryConstructor;
|
||||||
import android.pim.vcard.VCardEntryHandler;
|
import android.pim.vcard.VCardEntryHandler;
|
||||||
import android.pim.vcard.VCardParser;
|
import android.pim.vcard.VCardParser;
|
||||||
import android.pim.vcard.VCardParser_V21;
|
import android.pim.vcard.VCardUtils;
|
||||||
import android.pim.vcard.VCardParser_V30;
|
|
||||||
import android.pim.vcard.exception.VCardException;
|
import android.pim.vcard.exception.VCardException;
|
||||||
import android.provider.ContactsContract.Data;
|
import android.provider.ContactsContract.Data;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
@@ -31,7 +29,7 @@ import android.test.AndroidTestCase;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
/* package */ class ContentValuesVerifierElem {
|
public class ContentValuesVerifierElem {
|
||||||
private final AndroidTestCase mTestCase;
|
private final AndroidTestCase mTestCase;
|
||||||
private final ImportTestResolver mResolver;
|
private final ImportTestResolver mResolver;
|
||||||
private final VCardEntryHandler mHandler;
|
private final VCardEntryHandler mHandler;
|
||||||
@@ -49,20 +47,14 @@ import java.io.InputStream;
|
|||||||
return new ContentValuesBuilder(contentValues);
|
return new ContentValuesBuilder(contentValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void verify(int resId, int vCardType)
|
public void verify(int resId, int vcardType)
|
||||||
throws IOException, VCardException {
|
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 {
|
public void verify(InputStream is, int vcardType) throws IOException, VCardException {
|
||||||
final VCardParser vCardParser;
|
final VCardParser vCardParser = VCardUtils.getAppropriateParser(vcardType);
|
||||||
if (VCardConfig.isV30(vCardType)) {
|
final VCardEntryConstructor builder = new VCardEntryConstructor(vcardType, null);
|
||||||
vCardParser = new VCardParser_V30(true); // use StrictParsing
|
|
||||||
} else {
|
|
||||||
vCardParser = new VCardParser_V21();
|
|
||||||
}
|
|
||||||
VCardEntryConstructor builder =
|
|
||||||
new VCardEntryConstructor(null, null, false, vCardType, null);
|
|
||||||
builder.addEntryHandler(mHandler);
|
builder.addEntryHandler(mHandler);
|
||||||
try {
|
try {
|
||||||
vCardParser.parse(is, builder);
|
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
|
* 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
|
* 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
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@@ -21,11 +21,12 @@ import android.content.Entity;
|
|||||||
import android.content.EntityIterator;
|
import android.content.EntityIterator;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.pim.vcard.VCardComposer;
|
||||||
import android.provider.ContactsContract.Contacts;
|
import android.provider.ContactsContract.Contacts;
|
||||||
import android.provider.ContactsContract.Data;
|
import android.provider.ContactsContract.Data;
|
||||||
import android.provider.ContactsContract.RawContacts;
|
import android.provider.ContactsContract.RawContacts;
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
import android.test.mock.MockContentProvider;
|
import android.test.mock.MockContentProvider;
|
||||||
import android.test.mock.MockContentResolver;
|
|
||||||
import android.test.mock.MockCursor;
|
import android.test.mock.MockCursor;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
@@ -34,82 +35,44 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/* package */ public class ExportTestResolver extends MockContentResolver {
|
public class ExportTestProvider extends MockContentProvider {
|
||||||
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;
|
|
||||||
final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
|
final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
|
||||||
|
|
||||||
public ExportTestProvider(TestCase testCase) {
|
private static class MockEntityIterator implements EntityIterator {
|
||||||
mTestCase = testCase;
|
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() {
|
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
|
* 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.
|
* which value in the ContentValue corresponds to the entry in Contacts database.
|
||||||
* </p>
|
* </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,
|
public EntityIterator queryEntities(Uri uri,
|
||||||
String selection, String[] selectionArgs, String sortOrder) {
|
String selection, String[] selectionArgs, String sortOrder) {
|
||||||
mTestCase.assertTrue(uri != null);
|
TestCase.assertTrue(uri != null);
|
||||||
mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
|
TestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
|
||||||
final String authority = uri.getAuthority();
|
final String authority = uri.getAuthority();
|
||||||
mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority));
|
TestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority));
|
||||||
mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection));
|
TestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection));
|
||||||
mTestCase.assertEquals(1, selectionArgs.length);
|
TestCase.assertEquals(1, selectionArgs.length);
|
||||||
final int id = Integer.parseInt(selectionArgs[0]);
|
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());
|
return new MockEntityIterator(mContactEntryList.get(id).getList());
|
||||||
}
|
}
|
||||||
@@ -154,11 +107,11 @@ import java.util.List;
|
|||||||
@Override
|
@Override
|
||||||
public Cursor query(Uri uri,String[] projection,
|
public Cursor query(Uri uri,String[] projection,
|
||||||
String selection, String[] selectionArgs, String sortOrder) {
|
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.
|
// In this test, following arguments are not supported.
|
||||||
mTestCase.assertNull(selection);
|
TestCase.assertNull(selection);
|
||||||
mTestCase.assertNull(selectionArgs);
|
TestCase.assertNull(selectionArgs);
|
||||||
mTestCase.assertNull(sortOrder);
|
TestCase.assertNull(sortOrder);
|
||||||
|
|
||||||
return new MockCursor() {
|
return new MockCursor() {
|
||||||
int mCurrentPosition = -1;
|
int mCurrentPosition = -1;
|
||||||
@@ -196,14 +149,14 @@ import java.util.List;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getColumnIndex(String columnName) {
|
public int getColumnIndex(String columnName) {
|
||||||
mTestCase.assertEquals(Contacts._ID, columnName);
|
TestCase.assertEquals(Contacts._ID, columnName);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInt(int columnIndex) {
|
public int getInt(int columnIndex) {
|
||||||
mTestCase.assertEquals(0, columnIndex);
|
TestCase.assertEquals(0, columnIndex);
|
||||||
mTestCase.assertTrue(mCurrentPosition >= 0
|
TestCase.assertTrue(mCurrentPosition >= 0
|
||||||
&& mCurrentPosition < mContactEntryList.size());
|
&& mCurrentPosition < mContactEntryList.size());
|
||||||
return mCurrentPosition;
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.ContentProviderOperation;
|
import android.content.ContentProviderOperation;
|
||||||
import android.content.ContentProviderResult;
|
import android.content.ContentProviderResult;
|
||||||
@@ -34,8 +34,8 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
|||||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||||
import android.provider.ContactsContract.Data;
|
import android.provider.ContactsContract.Data;
|
||||||
import android.provider.ContactsContract.RawContacts;
|
import android.provider.ContactsContract.RawContacts;
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
import android.test.mock.MockContentProvider;
|
import android.test.mock.MockContentProvider;
|
||||||
import android.test.mock.MockContentResolver;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
@@ -51,38 +51,7 @@ import java.util.Set;
|
|||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
/* package */ class ImportTestResolver extends MockContentResolver {
|
public class ImportTestProvider extends MockContentProvider {
|
||||||
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 {
|
|
||||||
private static final Set<String> sKnownMimeTypeSet =
|
private static final Set<String> sKnownMimeTypeSet =
|
||||||
new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
|
new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
|
||||||
Nickname.CONTENT_ITEM_TYPE, Phone.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;
|
final Map<String, Collection<ContentValues>> mMimeTypeToExpectedContentValues;
|
||||||
|
|
||||||
private final TestCase mTestCase;
|
public ImportTestProvider(AndroidTestCase androidTestCase) {
|
||||||
|
|
||||||
public ImportTestProvider(TestCase testCase) {
|
|
||||||
mTestCase = testCase;
|
|
||||||
mMimeTypeToExpectedContentValues =
|
mMimeTypeToExpectedContentValues =
|
||||||
new HashMap<String, Collection<ContentValues>>();
|
new HashMap<String, Collection<ContentValues>>();
|
||||||
for (String acceptanbleMimeType : sKnownMimeTypeSet) {
|
for (String acceptanbleMimeType : sKnownMimeTypeSet) {
|
||||||
@@ -113,7 +79,7 @@ import java.util.TreeMap;
|
|||||||
public void addExpectedContentValues(ContentValues expectedContentValues) {
|
public void addExpectedContentValues(ContentValues expectedContentValues) {
|
||||||
final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE);
|
final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE);
|
||||||
if (!sKnownMimeTypeSet.contains(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.",
|
"Unknow MimeType %s in the test code. Test code should be broken.",
|
||||||
mimeType));
|
mimeType));
|
||||||
}
|
}
|
||||||
@@ -127,7 +93,7 @@ import java.util.TreeMap;
|
|||||||
public ContentProviderResult[] applyBatch(
|
public ContentProviderResult[] applyBatch(
|
||||||
ArrayList<ContentProviderOperation> operations) {
|
ArrayList<ContentProviderOperation> operations) {
|
||||||
if (operations == null) {
|
if (operations == null) {
|
||||||
mTestCase.fail("There is no operation.");
|
TestCase.fail("There is no operation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final int size = operations.size();
|
final int size = operations.size();
|
||||||
@@ -148,12 +114,12 @@ import java.util.TreeMap;
|
|||||||
fakeResultArray, i);
|
fakeResultArray, i);
|
||||||
final Uri uri = operation.getUri();
|
final Uri uri = operation.getUri();
|
||||||
if (uri.equals(RawContacts.CONTENT_URI)) {
|
if (uri.equals(RawContacts.CONTENT_URI)) {
|
||||||
mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
|
TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
|
||||||
mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
|
TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
|
||||||
} else if (uri.equals(Data.CONTENT_URI)) {
|
} else if (uri.equals(Data.CONTENT_URI)) {
|
||||||
final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
|
final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
|
||||||
if (!sKnownMimeTypeSet.contains(mimeType)) {
|
if (!sKnownMimeTypeSet.contains(mimeType)) {
|
||||||
mTestCase.fail(String.format(
|
TestCase.fail(String.format(
|
||||||
"Unknown MimeType %s. Probably added after developing this test",
|
"Unknown MimeType %s. Probably added after developing this test",
|
||||||
mimeType));
|
mimeType));
|
||||||
}
|
}
|
||||||
@@ -185,7 +151,7 @@ import java.util.TreeMap;
|
|||||||
final Collection<ContentValues> contentValuesCollection =
|
final Collection<ContentValues> contentValuesCollection =
|
||||||
mMimeTypeToExpectedContentValues.get(mimeType);
|
mMimeTypeToExpectedContentValues.get(mimeType);
|
||||||
if (contentValuesCollection.isEmpty()) {
|
if (contentValuesCollection.isEmpty()) {
|
||||||
mTestCase.fail("ContentValues for MimeType " + mimeType
|
TestCase.fail("ContentValues for MimeType " + mimeType
|
||||||
+ " is not expected at all (" + actualContentValues + ")");
|
+ " is not expected at all (" + actualContentValues + ")");
|
||||||
}
|
}
|
||||||
boolean checked = false;
|
boolean checked = false;
|
||||||
@@ -197,23 +163,25 @@ import java.util.TreeMap;
|
|||||||
+ convertToEasilyReadableString(actualContentValues));*/
|
+ convertToEasilyReadableString(actualContentValues));*/
|
||||||
if (equalsForContentValues(expectedContentValues,
|
if (equalsForContentValues(expectedContentValues,
|
||||||
actualContentValues)) {
|
actualContentValues)) {
|
||||||
mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
|
TestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
|
||||||
checked = true;
|
checked = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!checked) {
|
if (!checked) {
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("\n");
|
||||||
builder.append("Unexpected: ");
|
builder.append("Unexpected: ");
|
||||||
builder.append(convertToEasilyReadableString(actualContentValues));
|
builder.append(convertToEasilyReadableString(actualContentValues));
|
||||||
builder.append("\nExpected: ");
|
builder.append("\n");
|
||||||
|
builder.append("Expected : ");
|
||||||
for (ContentValues expectedContentValues : contentValuesCollection) {
|
for (ContentValues expectedContentValues : contentValuesCollection) {
|
||||||
builder.append(convertToEasilyReadableString(expectedContentValues));
|
builder.append(convertToEasilyReadableString(expectedContentValues));
|
||||||
}
|
}
|
||||||
mTestCase.fail(builder.toString());
|
TestCase.fail(builder.toString());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mTestCase.fail("Unexpected Uri has come: " + uri);
|
TestCase.fail("Unexpected Uri has come: " + uri);
|
||||||
}
|
}
|
||||||
} // for (int i = 0; i < size; i++) {
|
} // for (int i = 0; i < size; i++) {
|
||||||
return fakeResultArray;
|
return fakeResultArray;
|
||||||
@@ -232,7 +200,7 @@ import java.util.TreeMap;
|
|||||||
final String failMsg =
|
final String failMsg =
|
||||||
"There is(are) remaining expected ContentValues instance(s): \n"
|
"There is(are) remaining expected ContentValues instance(s): \n"
|
||||||
+ builder.toString();
|
+ builder.toString();
|
||||||
mTestCase.fail(failMsg);
|
TestCase.fail(failMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +220,7 @@ import java.util.TreeMap;
|
|||||||
if (Data.MIMETYPE.equals(key)) {
|
if (Data.MIMETYPE.equals(key)) {
|
||||||
mimeTypeValue = valueString;
|
mimeTypeValue = valueString;
|
||||||
} else {
|
} else {
|
||||||
mTestCase.assertNotNull(key);
|
TestCase.assertNotNull(key);
|
||||||
sortedMap.put(key, valueString);
|
sortedMap.put(key, valueString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,7 +241,7 @@ import java.util.TreeMap;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean equalsForContentValues(
|
private static boolean equalsForContentValues(
|
||||||
ContentValues expected, ContentValues actual) {
|
final ContentValues expected, final ContentValues actual) {
|
||||||
if (expected == actual) {
|
if (expected == actual) {
|
||||||
return true;
|
return true;
|
||||||
} else if (expected == null || actual == null || expected.size() != actual.size()) {
|
} else if (expected == null || actual == null || expected.size() != actual.size()) {
|
||||||
@@ -286,15 +254,21 @@ import java.util.TreeMap;
|
|||||||
if (!actual.containsKey(key)) {
|
if (!actual.containsKey(key)) {
|
||||||
return false;
|
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[]) {
|
if (value instanceof byte[]) {
|
||||||
Object actualValue = actual.get(key);
|
|
||||||
if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
|
if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
|
||||||
|
byte[] e = (byte[])value;
|
||||||
|
byte[] a = (byte[])actualValue;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!value.equals(actual.get(key))) {
|
} else if (!value.equals(actualValue) &&
|
||||||
return false;
|
!value.toString().equals(actualValue.toString())) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.pim.vcard.VCardComposer;
|
import android.pim.vcard.VCardComposer;
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
class LineVerifier implements VCardComposer.OneEntryHandler {
|
public class LineVerifier implements VCardComposer.OneEntryHandler {
|
||||||
private final TestCase mTestCase;
|
private final AndroidTestCase mAndroidTestCase;
|
||||||
private final ArrayList<LineVerifierElem> mLineVerifierElemList;
|
private final ArrayList<LineVerifierElem> mLineVerifierElemList;
|
||||||
private int mVCardType;
|
private int mVCardType;
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
public LineVerifier(TestCase testCase, int vcardType) {
|
public LineVerifier(AndroidTestCase androidTestCase, int vcardType) {
|
||||||
mTestCase = testCase;
|
mAndroidTestCase = androidTestCase;
|
||||||
mLineVerifierElemList = new ArrayList<LineVerifierElem>();
|
mLineVerifierElemList = new ArrayList<LineVerifierElem>();
|
||||||
mVCardType = vcardType;
|
mVCardType = vcardType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LineVerifierElem addLineVerifierElem() {
|
public LineVerifierElem addLineVerifierElem() {
|
||||||
LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType);
|
LineVerifierElem lineVerifier = new LineVerifierElem(mAndroidTestCase, mVCardType);
|
||||||
mLineVerifierElemList.add(lineVerifier);
|
mLineVerifierElemList.add(lineVerifier);
|
||||||
return lineVerifier;
|
return lineVerifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void verify(String vcard) {
|
public void verify(String vcard) {
|
||||||
if (index >= mLineVerifierElemList.size()) {
|
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);
|
lineVerifier.verify(vcard);
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
@@ -13,9 +13,10 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.pim.vcard.VCardConfig;
|
import android.pim.vcard.VCardConfig;
|
||||||
|
import android.test.AndroidTestCase;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
@@ -23,14 +24,12 @@ import junit.framework.TestCase;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class LineVerifierElem {
|
public class LineVerifierElem {
|
||||||
private final TestCase mTestCase;
|
|
||||||
private final List<String> mExpectedLineList = new ArrayList<String>();
|
private final List<String> mExpectedLineList = new ArrayList<String>();
|
||||||
private final boolean mIsV30;
|
private final int mVCardType;
|
||||||
|
|
||||||
public LineVerifierElem(TestCase testCase, int vcardType) {
|
public LineVerifierElem(AndroidTestCase androidTestCase, int vcardType) {
|
||||||
mTestCase = testCase;
|
mVCardType = vcardType;
|
||||||
mIsV30 = VCardConfig.isV30(vcardType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LineVerifierElem addExpected(final String line) {
|
public LineVerifierElem addExpected(final String line) {
|
||||||
@@ -55,21 +54,23 @@ class LineVerifierElem {
|
|||||||
|
|
||||||
if ("BEGIN:VCARD".equalsIgnoreCase(line)) {
|
if ("BEGIN:VCARD".equalsIgnoreCase(line)) {
|
||||||
if (beginExists) {
|
if (beginExists) {
|
||||||
mTestCase.fail("Multiple \"BEGIN:VCARD\" line found");
|
TestCase.fail("Multiple \"BEGIN:VCARD\" line found");
|
||||||
} else {
|
} else {
|
||||||
beginExists = true;
|
beginExists = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if ("END:VCARD".equalsIgnoreCase(line)) {
|
} else if ("END:VCARD".equalsIgnoreCase(line)) {
|
||||||
if (endExists) {
|
if (endExists) {
|
||||||
mTestCase.fail("Multiple \"END:VCARD\" line found");
|
TestCase.fail("Multiple \"END:VCARD\" line found");
|
||||||
} else {
|
} else {
|
||||||
endExists = true;
|
endExists = true;
|
||||||
continue;
|
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) {
|
if (versionExists) {
|
||||||
mTestCase.fail("Multiple VERSION line + found");
|
TestCase.fail("Multiple VERSION line + found");
|
||||||
} else {
|
} else {
|
||||||
versionExists = true;
|
versionExists = true;
|
||||||
continue;
|
continue;
|
||||||
@@ -77,18 +78,16 @@ class LineVerifierElem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!beginExists) {
|
if (!beginExists) {
|
||||||
mTestCase.fail("Property other than BEGIN came before BEGIN property: "
|
TestCase.fail("Property other than BEGIN came before BEGIN property: " + line);
|
||||||
+ line);
|
|
||||||
} else if (endExists) {
|
} else if (endExists) {
|
||||||
mTestCase.fail("Property other than END came after END property: "
|
TestCase.fail("Property other than END came after END property: " + line);
|
||||||
+ line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final int index = mExpectedLineList.indexOf(line);
|
final int index = mExpectedLineList.indexOf(line);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
mExpectedLineList.remove(index);
|
mExpectedLineList.remove(index);
|
||||||
} else {
|
} else {
|
||||||
mTestCase.fail("Unexpected line: " + line);
|
TestCase.fail("Unexpected line: " + line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +98,7 @@ class LineVerifierElem {
|
|||||||
buffer.append("\n");
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.pim.vcard.VCardEntry;
|
import android.pim.vcard.VCardEntry;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -26,12 +25,18 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
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.
|
* 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
|
* 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
|
* 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
|
* 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 class PropertyNode {
|
||||||
public String propName;
|
public String propName;
|
||||||
@@ -43,7 +48,8 @@ public class PropertyNode {
|
|||||||
*/
|
*/
|
||||||
public byte[] propValue_bytes;
|
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
|
* 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
|
* defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
|
||||||
* one String value like "A,B", not ["A", "B"]...
|
* 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
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 android.test.AndroidTestCase;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
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.
|
* 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
|
* 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.
|
* "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 static class TypeSet extends HashSet<String> {
|
||||||
public TypeSet(String ... array) {
|
public TypeSet(String ... array) {
|
||||||
super(Arrays.asList(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
|
// Intentionally use ArrayList instead of Set, assuming there may be more than one
|
||||||
// exactly same objects.
|
// exactly same objects.
|
||||||
private final ArrayList<PropertyNode> mUnorderedNodeList;
|
private final ArrayList<PropertyNode> mUnorderedNodeList;
|
||||||
private final TestCase mTestCase;
|
|
||||||
|
|
||||||
public PropertyNodesVerifierElem(TestCase testCase) {
|
public PropertyNodesVerifierElem(AndroidTestCase androidTestCase) {
|
||||||
mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
|
mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
|
||||||
mUnorderedNodeList = new ArrayList<PropertyNode>();
|
mUnorderedNodeList = new ArrayList<PropertyNode>();
|
||||||
mTestCase = testCase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithOrder
|
// WithOrder
|
||||||
@@ -170,6 +100,12 @@ import java.util.List;
|
|||||||
paramMap_TYPE, null);
|
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,
|
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||||
ContentValues paramMap, TypeSet paramMap_TYPE) {
|
ContentValues paramMap, TypeSet paramMap_TYPE) {
|
||||||
return addExpectedNodeWithOrder(propName, propValue, null, null,
|
return addExpectedNodeWithOrder(propName, propValue, null, null,
|
||||||
@@ -182,13 +118,19 @@ import java.util.List;
|
|||||||
paramMap_TYPE, null);
|
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,
|
public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
|
||||||
List<String> propValueList, byte[] propValue_bytes,
|
List<String> propValueList, byte[] propValue_bytes,
|
||||||
ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
|
ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
|
||||||
if (propValue == null && propValueList != null) {
|
if (propValue == null && propValueList != null) {
|
||||||
propValue = concatinateListWithSemiColon(propValueList);
|
propValue = concatinateListWithSemiColon(propValueList);
|
||||||
}
|
}
|
||||||
PropertyNode propertyNode = new PropertyNode(propName,
|
final PropertyNode propertyNode = new PropertyNode(propName,
|
||||||
propValue, propValueList, propValue_bytes,
|
propValue, propValueList, propValue_bytes,
|
||||||
paramMap, paramMap_TYPE, propGroupSet);
|
paramMap, paramMap_TYPE, propGroupSet);
|
||||||
List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
|
List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
|
||||||
@@ -267,8 +209,9 @@ import java.util.List;
|
|||||||
for (PropertyNode actualNode : vnode.propList) {
|
for (PropertyNode actualNode : vnode.propList) {
|
||||||
verifyNode(actualNode.propName, actualNode);
|
verifyNode(actualNode.propName, actualNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) {
|
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 (List<PropertyNode> nodes : mOrderedNodeMap.values()) {
|
||||||
for (PropertyNode node : nodes) {
|
for (PropertyNode node : nodes) {
|
||||||
if (!expectedProps.contains(node.propName)) {
|
if (!expectedProps.contains(node.propName)) {
|
||||||
@@ -281,18 +224,19 @@ import java.util.List;
|
|||||||
expectedProps.add(node.propName);
|
expectedProps.add(node.propName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
|
TestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
|
||||||
+ " was not found.");
|
+ " was not found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyNode(final String propName, final PropertyNode actualNode) {
|
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);
|
final int size = (expectedNodeList != null ? expectedNodeList.size() : 0);
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
PropertyNode expectedNode = expectedNodeList.get(i);
|
final PropertyNode expectedNode = expectedNodeList.get(i);
|
||||||
List<PropertyNode> expectedButDifferentValueList = new ArrayList<PropertyNode>();
|
final List<PropertyNode> expectedButDifferentValueList =
|
||||||
|
new ArrayList<PropertyNode>();
|
||||||
if (expectedNode.propName.equals(propName)) {
|
if (expectedNode.propName.equals(propName)) {
|
||||||
if (expectedNode.equals(actualNode)) {
|
if (expectedNode.equals(actualNode)) {
|
||||||
expectedNodeList.remove(i);
|
expectedNodeList.remove(i);
|
||||||
@@ -318,7 +262,7 @@ import java.util.List;
|
|||||||
expectedButDifferentValueList);
|
expectedButDifferentValueList);
|
||||||
} else {
|
} else {
|
||||||
// There's no expected node with same propName.
|
// There's no expected node with same propName.
|
||||||
mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
TestCase.fail("Unexpected property \"" + propName + "\" exists.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -333,7 +277,7 @@ import java.util.List;
|
|||||||
expectedButDifferentValueList);
|
expectedButDifferentValueList);
|
||||||
} else {
|
} else {
|
||||||
// There's no expected node with same propName.
|
// 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(expectedNode.toString());
|
||||||
builder.append("\n");
|
builder.append("\n");
|
||||||
}
|
}
|
||||||
mTestCase.fail("Property \"" + propName + "\" has wrong value.\n"
|
TestCase.fail("Property \"" + propName + "\" has wrong value.\n"
|
||||||
+ builder.toString()
|
+ builder.toString()
|
||||||
+ " actual: " + actualNode.toString());
|
+ " actual: " + actualNode.toString());
|
||||||
}
|
}
|
||||||
@@ -13,10 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package android.pim.vcard.test_utils;
|
||||||
package android.pim.vcard;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.pim.vcard.VCardConfig;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,8 +24,9 @@ import android.test.AndroidTestCase;
|
|||||||
* Please do not add each unit test here.
|
* Please do not add each unit test here.
|
||||||
*/
|
*/
|
||||||
public class VCardTestsBase extends AndroidTestCase {
|
public class VCardTestsBase extends AndroidTestCase {
|
||||||
public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8;
|
public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC;
|
||||||
public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8;
|
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.
|
// Do not modify these during tests.
|
||||||
protected final ContentValues mContentValuesForQP;
|
protected final ContentValues mContentValuesForQP;
|
||||||
@@ -41,6 +42,7 @@ public class VCardTestsBase extends AndroidTestCase {
|
|||||||
|
|
||||||
public VCardTestsBase() {
|
public VCardTestsBase() {
|
||||||
super();
|
super();
|
||||||
|
// Not using constants in vCard code since it may be wrong.
|
||||||
mContentValuesForQP = new ContentValues();
|
mContentValuesForQP = new ContentValues();
|
||||||
mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
|
mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
|
||||||
mContentValuesForSJis = new ContentValues();
|
mContentValuesForSJis = new ContentValues();
|
||||||
@@ -13,9 +13,8 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.EntityIterator;
|
import android.content.EntityIterator;
|
||||||
@@ -26,50 +25,62 @@ import android.pim.vcard.VCardEntryConstructor;
|
|||||||
import android.pim.vcard.VCardInterpreter;
|
import android.pim.vcard.VCardInterpreter;
|
||||||
import android.pim.vcard.VCardInterpreterCollection;
|
import android.pim.vcard.VCardInterpreterCollection;
|
||||||
import android.pim.vcard.VCardParser;
|
import android.pim.vcard.VCardParser;
|
||||||
import android.pim.vcard.VCardParser_V21;
|
import android.pim.vcard.VCardUtils;
|
||||||
import android.pim.vcard.VCardParser_V30;
|
|
||||||
import android.pim.vcard.exception.VCardException;
|
import android.pim.vcard.exception.VCardException;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.test.mock.MockContext;
|
import android.test.mock.MockContext;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/* package */ class CustomMockContext extends MockContext {
|
/**
|
||||||
final ContentResolver mResolver;
|
* <p>
|
||||||
public CustomMockContext(ContentResolver resolver) {
|
* The class lets users checks that given expected vCard data are same as given actual vCard data.
|
||||||
mResolver = resolver;
|
* Able to verify both vCard importer/exporter.
|
||||||
}
|
* </p>
|
||||||
|
* <p>
|
||||||
@Override
|
* First a user has to initialize the object by calling either
|
||||||
public ContentResolver getContentResolver() {
|
* {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}.
|
||||||
return mResolver;
|
* "Round trip test" (import -> export -> import, or export -> import -> export) is not supported.
|
||||||
}
|
* </p>
|
||||||
}
|
*/
|
||||||
|
public class VCardVerifier {
|
||||||
/* package */ class VCardVerifier {
|
|
||||||
private static final String LOG_TAG = "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 {
|
private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
|
||||||
public boolean onInit(Context context) {
|
public boolean onInit(Context context) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public boolean onEntryCreated(String vcard) {
|
public boolean onEntryCreated(String vcard) {
|
||||||
verifyOneVCard(vcard);
|
verifyOneVCardForExport(vcard);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public void onTerminate() {
|
public void onTerminate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AndroidTestCase mTestCase;
|
private final AndroidTestCase mAndroidTestCase;
|
||||||
private final VCardVerifierInternal mVCardVerifierInternal;
|
private final VCardVerifierInternal mVCardVerifierInternal;
|
||||||
private int mVCardType;
|
private int mVCardType;
|
||||||
private boolean mIsV30;
|
|
||||||
private boolean mIsDoCoMo;
|
private boolean mIsDoCoMo;
|
||||||
|
|
||||||
// Only one of them must be non-empty.
|
// Only one of them must be non-empty.
|
||||||
@@ -83,9 +94,11 @@ import java.util.Arrays;
|
|||||||
private ContentValuesVerifier mContentValuesVerifier;
|
private ContentValuesVerifier mContentValuesVerifier;
|
||||||
private boolean mInitialized;
|
private boolean mInitialized;
|
||||||
private boolean mVerified = false;
|
private boolean mVerified = false;
|
||||||
|
private String mCharset;
|
||||||
|
|
||||||
|
// Called by VCardTestsBase
|
||||||
public VCardVerifier(AndroidTestCase androidTestCase) {
|
public VCardVerifier(AndroidTestCase androidTestCase) {
|
||||||
mTestCase = androidTestCase;
|
mAndroidTestCase = androidTestCase;
|
||||||
mVCardVerifierInternal = new VCardVerifierInternal();
|
mVCardVerifierInternal = new VCardVerifierInternal();
|
||||||
mExportTestResolver = null;
|
mExportTestResolver = null;
|
||||||
mInputStream = null;
|
mInputStream = null;
|
||||||
@@ -93,76 +106,101 @@ import java.util.Arrays;
|
|||||||
mVerified = false;
|
mVerified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initForExportTest(int vcardType) {
|
// Should be called at the beginning of each import test.
|
||||||
if (mInitialized) {
|
|
||||||
mTestCase.fail("Already initialized");
|
|
||||||
}
|
|
||||||
mExportTestResolver = new ExportTestResolver(mTestCase);
|
|
||||||
mVCardType = vcardType;
|
|
||||||
mIsV30 = VCardConfig.isV30(vcardType);
|
|
||||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
|
||||||
mInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initForImportTest(int vcardType, int resId) {
|
public void initForImportTest(int vcardType, int resId) {
|
||||||
if (mInitialized) {
|
if (mInitialized) {
|
||||||
mTestCase.fail("Already initialized");
|
AndroidTestCase.fail("Already initialized");
|
||||||
}
|
}
|
||||||
mVCardType = vcardType;
|
mVCardType = vcardType;
|
||||||
mIsV30 = VCardConfig.isV30(vcardType);
|
|
||||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||||
setInputResourceId(resId);
|
setInputResourceId(resId);
|
||||||
mInitialized = true;
|
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) {
|
private void setInputResourceId(int resId) {
|
||||||
InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId);
|
InputStream inputStream = mAndroidTestCase.getContext().getResources().openRawResource(resId);
|
||||||
if (inputStream == null) {
|
if (inputStream == null) {
|
||||||
mTestCase.fail("Wrong resId: " + resId);
|
AndroidTestCase.fail("Wrong resId: " + resId);
|
||||||
}
|
}
|
||||||
setInputStream(inputStream);
|
setInputStream(inputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInputStream(InputStream inputStream) {
|
private void setInputStream(InputStream inputStream) {
|
||||||
if (mExportTestResolver != null) {
|
if (mExportTestResolver != null) {
|
||||||
mTestCase.fail("addInputEntry() is called.");
|
AndroidTestCase.fail("addInputEntry() is called.");
|
||||||
} else if (mInputStream != null) {
|
} else if (mInputStream != null) {
|
||||||
mTestCase.fail("InputStream is already set");
|
AndroidTestCase.fail("InputStream is already set");
|
||||||
}
|
}
|
||||||
mInputStream = inputStream;
|
mInputStream = inputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContactEntry addInputEntry() {
|
public ContactEntry addInputEntry() {
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
mTestCase.fail("Not initialized");
|
AndroidTestCase.fail("Not initialized");
|
||||||
}
|
}
|
||||||
if (mInputStream != null) {
|
if (mInputStream != null) {
|
||||||
mTestCase.fail("setInputStream is called");
|
AndroidTestCase.fail("setInputStream is called");
|
||||||
}
|
}
|
||||||
return mExportTestResolver.addInputContactEntry();
|
return mExportTestResolver.addInputContactEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
|
public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithoutVersion() {
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
mTestCase.fail("Not initialized");
|
AndroidTestCase.fail("Not initialized");
|
||||||
}
|
}
|
||||||
if (mPropertyNodesVerifier == null) {
|
if (mPropertyNodesVerifier == null) {
|
||||||
mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase);
|
mPropertyNodesVerifier = new PropertyNodesVerifier(mAndroidTestCase);
|
||||||
}
|
}
|
||||||
PropertyNodesVerifierElem elem =
|
return mPropertyNodesVerifier.addPropertyNodesVerifierElem();
|
||||||
mPropertyNodesVerifier.addPropertyNodesVerifierElem();
|
}
|
||||||
elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1"));
|
|
||||||
|
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;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
|
public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
mTestCase.fail("Not initialized");
|
AndroidTestCase.fail("Not initialized");
|
||||||
}
|
}
|
||||||
PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
|
final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
|
||||||
if (mIsV30) {
|
if (VCardConfig.isVersion40(mVCardType)) {
|
||||||
elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", "");
|
elem.addExpectedNodeWithOrder("FN", "");
|
||||||
|
} else if (VCardConfig.isVersion30(mVCardType)) {
|
||||||
|
elem.addExpectedNodeWithOrder("N", "");
|
||||||
|
elem.addExpectedNodeWithOrder("FN", "");
|
||||||
} else if (mIsDoCoMo) {
|
} else if (mIsDoCoMo) {
|
||||||
elem.addExpectedNodeWithOrder("N", "");
|
elem.addExpectedNodeWithOrder("N", "");
|
||||||
}
|
}
|
||||||
@@ -171,64 +209,75 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public LineVerifierElem addLineVerifierElem() {
|
public LineVerifierElem addLineVerifierElem() {
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
mTestCase.fail("Not initialized");
|
AndroidTestCase.fail("Not initialized");
|
||||||
}
|
}
|
||||||
if (mLineVerifier == null) {
|
if (mLineVerifier == null) {
|
||||||
mLineVerifier = new LineVerifier(mTestCase, mVCardType);
|
mLineVerifier = new LineVerifier(mAndroidTestCase, mVCardType);
|
||||||
}
|
}
|
||||||
return mLineVerifier.addLineVerifierElem();
|
return mLineVerifier.addLineVerifierElem();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentValuesVerifierElem addContentValuesVerifierElem() {
|
public ContentValuesVerifierElem addContentValuesVerifierElem() {
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
mTestCase.fail("Not initialized");
|
AndroidTestCase.fail("Not initialized");
|
||||||
}
|
}
|
||||||
if (mContentValuesVerifier == null) {
|
if (mContentValuesVerifier == null) {
|
||||||
mContentValuesVerifier = new ContentValuesVerifier();
|
mContentValuesVerifier = new ContentValuesVerifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mContentValuesVerifier.addElem(mTestCase);
|
return mContentValuesVerifier.addElem(mAndroidTestCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyOneVCard(final String vcard) {
|
/**
|
||||||
// Log.d("@@@", vcard);
|
* Sets up sub-verifiers correctly and try parse given vCard as InputStream.
|
||||||
final VCardInterpreter builder;
|
* 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) {
|
if (mContentValuesVerifier != null) {
|
||||||
final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier;
|
final VCardEntryConstructor constructor = new VCardEntryConstructor(mVCardType);
|
||||||
final VCardEntryConstructor vcardDataBuilder =
|
constructor.addEntryHandler(mContentValuesVerifier);
|
||||||
new VCardEntryConstructor(mVCardType);
|
|
||||||
vcardDataBuilder.addEntryHandler(mContentValuesVerifier);
|
|
||||||
if (mPropertyNodesVerifier != null) {
|
if (mPropertyNodesVerifier != null) {
|
||||||
builder = new VCardInterpreterCollection(Arrays.asList(
|
interpreter = new VCardInterpreterCollection(Arrays.asList(
|
||||||
mPropertyNodesVerifier, vcardDataBuilder));
|
mPropertyNodesVerifier, constructor));
|
||||||
} else {
|
} else {
|
||||||
builder = vnodeBuilder;
|
interpreter = constructor;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mPropertyNodesVerifier != null) {
|
if (mPropertyNodesVerifier != null) {
|
||||||
builder = mPropertyNodesVerifier;
|
interpreter = mPropertyNodesVerifier;
|
||||||
} else {
|
} else {
|
||||||
return;
|
interpreter = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final VCardParser parser =
|
try {
|
||||||
(mIsV30 ? new VCardParser_V30(true) : new VCardParser_V21());
|
// 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;
|
InputStream is = null;
|
||||||
try {
|
try {
|
||||||
String charset =
|
is = new ByteArrayInputStream(vcard.getBytes(mCharset));
|
||||||
(VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8");
|
verifyWithInputStream(is);
|
||||||
is = new ByteArrayInputStream(vcard.getBytes(charset));
|
|
||||||
mTestCase.assertEquals(true, parser.parse(is, null, builder));
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
mTestCase.fail("Unexpected IOException: " + e.getMessage());
|
AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
|
||||||
} catch (VCardException e) {
|
|
||||||
mTestCase.fail("Unexpected VCardException: " + e.getMessage());
|
|
||||||
} finally {
|
} finally {
|
||||||
if (is != null) {
|
if (is != null) {
|
||||||
try {
|
try {
|
||||||
is.close();
|
is.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,33 +285,41 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public void verify() {
|
public void verify() {
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
mTestCase.fail("Not initialized.");
|
TestCase.fail("Not initialized.");
|
||||||
}
|
}
|
||||||
if (mVerified) {
|
if (mVerified) {
|
||||||
mTestCase.fail("verify() was called twice.");
|
TestCase.fail("verify() was called twice.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mInputStream != null) {
|
if (mInputStream != null) {
|
||||||
try {
|
if (mExportTestResolver != null){
|
||||||
verifyForImportTest();
|
TestCase.fail("There are two input sources.");
|
||||||
} catch (IOException e) {
|
|
||||||
mTestCase.fail("IOException was thrown: " + e.getMessage());
|
|
||||||
} catch (VCardException e) {
|
|
||||||
mTestCase.fail("VCardException was thrown: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
verifyForImportTest();
|
||||||
} else if (mExportTestResolver != null){
|
} else if (mExportTestResolver != null){
|
||||||
verifyForExportTest();
|
verifyForExportTest();
|
||||||
} else {
|
} else {
|
||||||
mTestCase.fail("No input is determined");
|
TestCase.fail("No input is determined");
|
||||||
}
|
}
|
||||||
mVerified = true;
|
mVerified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyForImportTest() throws IOException, VCardException {
|
private void verifyForImportTest() {
|
||||||
if (mLineVerifier != null) {
|
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() {
|
private void verifyForExportTest() {
|
||||||
final VCardComposer composer =
|
final VCardComposer composer =
|
||||||
new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType);
|
new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset);
|
||||||
composer.addHandler(mLineVerifier);
|
composer.addHandler(mLineVerifier);
|
||||||
composer.addHandler(mVCardVerifierInternal);
|
composer.addHandler(mVCardVerifierInternal);
|
||||||
if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
|
if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
|
||||||
@@ -303,7 +360,7 @@ import java.util.Arrays;
|
|||||||
composer.createOneEntry(mockGetEntityIteratorMethod));
|
composer.createOneEntry(mockGetEntityIteratorMethod));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
AndroidTestCase.fail();
|
AndroidTestCase.fail(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -13,18 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package android.pim.vcard;
|
package android.pim.vcard.test_utils;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.pim.vcard.VCardInterpreter;
|
|
||||||
import android.pim.vcard.VCardConfig;
|
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.CharsetUtils;
|
||||||
import android.util.Log;
|
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.io.UnsupportedEncodingException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@@ -32,34 +30,29 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
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.
|
* Maybe several vcard instance, so use vNodeList to store.
|
||||||
* VNode: standy by a vcard instance.
|
* </p>
|
||||||
* PropertyNode: standy by a property line of a card.
|
* <p>
|
||||||
*
|
* This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal).
|
||||||
* Previously used in main vCard handling code but now exists only for testing.
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class VNodeBuilder implements VCardInterpreter {
|
public class VNodeBuilder implements VCardInterpreter {
|
||||||
static private String LOG_TAG = "VNodeBuilder";
|
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>();
|
public List<VNode> vNodeList = new ArrayList<VNode>();
|
||||||
private int mNodeListPos = 0;
|
private int mNodeListPos = 0;
|
||||||
private VNode mCurrentVNode;
|
private VNode mCurrentVNode;
|
||||||
private PropertyNode mCurrentPropNode;
|
private PropertyNode mCurrentPropNode;
|
||||||
private String mCurrentParamType;
|
private String mCurrentParamType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The charset using which VParser parses the text.
|
* The charset using which VParser parses the text.
|
||||||
*/
|
*/
|
||||||
private String mSourceCharset;
|
private String mSourceCharset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The charset with which byte array is encoded to String.
|
* The charset with which byte array is encoded to String.
|
||||||
*/
|
*/
|
||||||
@@ -68,27 +61,15 @@ public class VNodeBuilder implements VCardInterpreter {
|
|||||||
private boolean mStrictLineBreakParsing;
|
private boolean mStrictLineBreakParsing;
|
||||||
|
|
||||||
public VNodeBuilder() {
|
public VNodeBuilder() {
|
||||||
this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false);
|
this(VCardConfig.DEFAULT_IMPORT_CHARSET, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VNodeBuilder(String charset, boolean strictLineBreakParsing) {
|
public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) {
|
||||||
this(null, charset, strictLineBreakParsing);
|
mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide sourceCharset is temporal.
|
|
||||||
*/
|
|
||||||
public VNodeBuilder(String sourceCharset, String targetCharset,
|
|
||||||
boolean strictLineBreakParsing) {
|
|
||||||
if (sourceCharset != null) {
|
|
||||||
mSourceCharset = sourceCharset;
|
|
||||||
} else {
|
|
||||||
mSourceCharset = VCardConfig.DEFAULT_CHARSET;
|
|
||||||
}
|
|
||||||
if (targetCharset != null) {
|
if (targetCharset != null) {
|
||||||
mTargetCharset = targetCharset;
|
mTargetCharset = targetCharset;
|
||||||
} else {
|
} else {
|
||||||
mTargetCharset = TARGET_CHARSET;
|
mTargetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
|
||||||
}
|
}
|
||||||
mStrictLineBreakParsing = strictLineBreakParsing;
|
mStrictLineBreakParsing = strictLineBreakParsing;
|
||||||
}
|
}
|
||||||
@@ -149,7 +130,6 @@ public class VNodeBuilder implements VCardInterpreter {
|
|||||||
mCurrentPropNode.propName = name;
|
mCurrentPropNode.propName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used only in VCard.
|
|
||||||
public void propertyGroup(String group) {
|
public void propertyGroup(String group) {
|
||||||
mCurrentPropNode.propGroupSet.add(group);
|
mCurrentPropNode.propGroupSet.add(group);
|
||||||
}
|
}
|
||||||
@@ -159,6 +139,12 @@ public class VNodeBuilder implements VCardInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void propertyParamValue(String value) {
|
public void propertyParamValue(String value) {
|
||||||
|
if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
|
||||||
|
value = VCardUtils.convertStringCharset(value,
|
||||||
|
VCardConfig.DEFAULT_INTERMEDIATE_CHARSET,
|
||||||
|
VCardConfig.DEFAULT_IMPORT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
if (mCurrentParamType == null ||
|
if (mCurrentParamType == null ||
|
||||||
mCurrentParamType.equalsIgnoreCase("TYPE")) {
|
mCurrentParamType.equalsIgnoreCase("TYPE")) {
|
||||||
mCurrentPropNode.paramMap_TYPE.add(value);
|
mCurrentPropNode.paramMap_TYPE.add(value);
|
||||||
@@ -192,71 +178,11 @@ public class VNodeBuilder implements VCardInterpreter {
|
|||||||
encoding = encoding.toUpperCase();
|
encoding = encoding.toUpperCase();
|
||||||
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
if (encoding.equals("BASE64") || encoding.equals("B")) {
|
||||||
// Assume BASE64 is used only when the number of values is 1.
|
// Assume BASE64 is used only when the number of values is 1.
|
||||||
mCurrentPropNode.propValue_bytes =
|
mCurrentPropNode.propValue_bytes = Base64.decode(value.getBytes(), Base64.NO_WRAP);
|
||||||
Base64.decodeBase64(value.getBytes());
|
|
||||||
return value;
|
return value;
|
||||||
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
} else if (encoding.equals("QUOTED-PRINTABLE")) {
|
||||||
String quotedPrintable = value
|
return VCardUtils.parseQuotedPrintable(
|
||||||
.replaceAll("= ", " ").replaceAll("=\t", "\t");
|
value, mStrictLineBreakParsing, mSourceCharset, targetCharset);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Unknown encoding. Fall back to default.
|
// Unknown encoding. Fall back to default.
|
||||||
}
|
}
|
||||||
@@ -309,6 +235,6 @@ public class VNodeBuilder implements VCardInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getResult(){
|
public String getResult(){
|
||||||
return null;
|
throw new RuntimeException("Not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user