Let vCard importer/exporter select charset.
There are some other bug where charset for import/export is used during vCard handling. Bug: 2572064 Change-Id: I0aef8a6eba0a7e9263e47368a43dbba05efa91b0
This commit is contained in:
@@ -47,7 +47,22 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The class which lets users create their own vCard String.
|
||||
* <p>
|
||||
* The class which lets users create their own vCard String. Typical usage is as follows:
|
||||
* </p>
|
||||
* <pre class="prettyprint">final VCardBuilder builder = new VCardBuilder(vcardType);
|
||||
* builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
|
||||
* .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
|
||||
* .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
|
||||
* .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
|
||||
* .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
|
||||
* .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
|
||||
* .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE))
|
||||
* .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE))
|
||||
* .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
|
||||
* .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
|
||||
* .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
|
||||
* .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE)); </pre>
|
||||
*/
|
||||
public class VCardBuilder {
|
||||
private static final String LOG_TAG = "VCardBuilder";
|
||||
@@ -81,7 +96,6 @@ public class VCardBuilder {
|
||||
private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b";
|
||||
|
||||
private static final String SHIFT_JIS = "SHIFT_JIS";
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
|
||||
private final int mVCardType;
|
||||
|
||||
@@ -92,21 +106,28 @@ public class VCardBuilder {
|
||||
private final boolean mShouldUseQuotedPrintable;
|
||||
private final boolean mUsesAndroidProperty;
|
||||
private final boolean mUsesDefactProperty;
|
||||
private final boolean mUsesUtf8;
|
||||
private final boolean mUsesShiftJis;
|
||||
private final boolean mAppendTypeParamName;
|
||||
private final boolean mRefrainsQPToNameProperties;
|
||||
private final boolean mNeedsToConvertPhoneticString;
|
||||
|
||||
private final boolean mShouldAppendCharsetParam;
|
||||
|
||||
private final String mCharsetString;
|
||||
private final String mCharset;
|
||||
private final String mVCardCharsetParameter;
|
||||
|
||||
private StringBuilder mBuilder;
|
||||
private boolean mEndAppended;
|
||||
|
||||
public VCardBuilder(final int vcardType) {
|
||||
// Default charset should be used
|
||||
this(vcardType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param vcardType
|
||||
* @param charset If null, we use default charset for export.
|
||||
*/
|
||||
public VCardBuilder(final int vcardType, String charset) {
|
||||
mVCardType = vcardType;
|
||||
|
||||
mIsV30 = VCardConfig.isV30(vcardType);
|
||||
@@ -116,40 +137,77 @@ public class VCardBuilder {
|
||||
mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType);
|
||||
mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType);
|
||||
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
|
||||
mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
|
||||
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
|
||||
mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType);
|
||||
mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
|
||||
mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType);
|
||||
|
||||
mShouldAppendCharsetParam = !(mIsV30 && mUsesUtf8);
|
||||
final boolean shouldUseUtf8 = VCardConfig.shouldUseUtf8ForExport(vcardType);
|
||||
final boolean shouldUseShiftJis = VCardConfig.shouldUseShiftJisForExport(vcardType);
|
||||
|
||||
if (mIsDoCoMo) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
// 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 = !(mIsV30 && shouldUseUtf8);
|
||||
|
||||
if (VCardConfig.isDoCoMo(vcardType) || shouldUseShiftJis) {
|
||||
if (!SHIFT_JIS.equalsIgnoreCase(charset)) {
|
||||
Log.w(LOG_TAG,
|
||||
"The charset \"" + charset + "\" is used while "
|
||||
+ SHIFT_JIS + " is needed to be used.");
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = SHIFT_JIS;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
} else {
|
||||
if (mIsDoCoMo) {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"DoCoMo-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"Career-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
mCharsetString = charset;
|
||||
// Do not use mCharsetString bellow since it is different from "SHIFT_JIS" but
|
||||
// may be "DOCOMO_SHIFT_JIS" or something like that (internal expression used in
|
||||
// Android, not shown to the public).
|
||||
mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
|
||||
} else if (mUsesShiftJis) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
mCharsetString = charset;
|
||||
mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
|
||||
} else {
|
||||
mCharsetString = UTF_8;
|
||||
mVCardCharsetParameter = "CHARSET=" + UTF_8;
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
Log.i(LOG_TAG,
|
||||
"Use the charset \"" + VCardConfig.DEFAULT_EXPORT_CHARSET
|
||||
+ "\" for export.");
|
||||
mCharset = VCardConfig.DEFAULT_EXPORT_CHARSET;
|
||||
mVCardCharsetParameter = "CHARSET=" + VCardConfig.DEFAULT_EXPORT_CHARSET;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
mVCardCharsetParameter = "CHARSET=" + charset;
|
||||
}
|
||||
}
|
||||
clear();
|
||||
}
|
||||
@@ -379,8 +437,8 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCardConstants.PROPERTY_FN);
|
||||
|
||||
// Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it
|
||||
// when it would be useful for external importers, assuming no external
|
||||
// importer allows this vioration.
|
||||
// when it would be useful or necessary for external importers,
|
||||
// assuming the external importer allows this vioration of the spec.
|
||||
if (shouldAppendCharsetParam(displayName)) {
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
mBuilder.append(mVCardCharsetParameter);
|
||||
@@ -454,18 +512,18 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
} else if (mIsJapaneseMobilePhone) {
|
||||
// Note: There is no appropriate property for expressing
|
||||
// phonetic name in vCard 2.1, while there is in
|
||||
// phonetic name (Yomigana in Japanese) in vCard 2.1, while there is in
|
||||
// vCard 3.0 (SORT-STRING).
|
||||
// We chose to use DoCoMo's way when the device is Japanese one
|
||||
// since it is supported by
|
||||
// a lot of Japanese mobile phones. This is "X-" property, so
|
||||
// any parser hopefully would not get confused with this.
|
||||
// We use DoCoMo's way when the device is Japanese one since it is already
|
||||
// supported by a lot of Japanese mobile phones.
|
||||
// This is "X-" property, so any parser hopefully would not get
|
||||
// confused with this.
|
||||
//
|
||||
// Also, DoCoMo's specification requires vCard composer to use just the first
|
||||
// column.
|
||||
// i.e.
|
||||
// o SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
|
||||
// x SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
|
||||
// good: SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
|
||||
// bad : SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
|
||||
mBuilder.append(VCardConstants.PROPERTY_SOUND);
|
||||
mBuilder.append(VCARD_PARAM_SEPARATOR);
|
||||
mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N);
|
||||
@@ -519,10 +577,10 @@ public class VCardBuilder {
|
||||
mBuilder.append(encodedPhoneticGivenName);
|
||||
}
|
||||
}
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR);
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // family;given
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // given;middle
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // middle;prefix
|
||||
mBuilder.append(VCARD_ITEM_SEPARATOR); // prefix;suffix
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
|
||||
@@ -549,7 +607,7 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(encodedPhoneticGivenName);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
} // if (!TextUtils.isEmpty(phoneticGivenName))
|
||||
if (!TextUtils.isEmpty(phoneticMiddleName)) {
|
||||
final boolean reallyUseQuotedPrintable =
|
||||
(mShouldUseQuotedPrintable &&
|
||||
@@ -572,7 +630,7 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(encodedPhoneticMiddleName);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
} // if (!TextUtils.isEmpty(phoneticGivenName))
|
||||
if (!TextUtils.isEmpty(phoneticFamilyName)) {
|
||||
final boolean reallyUseQuotedPrintable =
|
||||
(mShouldUseQuotedPrintable &&
|
||||
@@ -595,7 +653,7 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_DATA_SEPARATOR);
|
||||
mBuilder.append(encodedPhoneticFamilyName);
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
} // if (!TextUtils.isEmpty(phoneticFamilyName))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -903,21 +961,21 @@ public class VCardBuilder {
|
||||
encodedCountry = escapeCharacters(rawCountry);
|
||||
encodedNeighborhood = escapeCharacters(rawNeighborhood);
|
||||
}
|
||||
final StringBuffer addressBuffer = new StringBuffer();
|
||||
addressBuffer.append(encodedPoBox);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedStreet);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedLocality);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedRegion);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedPostalCode);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedCountry);
|
||||
final StringBuilder addressBuilder = new StringBuilder();
|
||||
addressBuilder.append(encodedPoBox);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street
|
||||
addressBuilder.append(encodedStreet);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality
|
||||
addressBuilder.append(encodedLocality);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region
|
||||
addressBuilder.append(encodedRegion);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code
|
||||
addressBuilder.append(encodedPostalCode);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country
|
||||
addressBuilder.append(encodedCountry);
|
||||
return new PostalStruct(
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuffer.toString());
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
|
||||
} else { // VCardUtils.areAllEmpty(rawAddressArray) == true
|
||||
// Try to use FORMATTED_ADDRESS instead.
|
||||
final String rawFormattedAddress =
|
||||
@@ -940,16 +998,16 @@ public class VCardBuilder {
|
||||
// We use the second value ("Extended Address") just because Japanese mobile phones
|
||||
// do so. If the other importer expects the value be in the other field, some flag may
|
||||
// be needed.
|
||||
final StringBuffer addressBuffer = new StringBuffer();
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(encodedFormattedAddress);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
addressBuffer.append(VCARD_ITEM_SEPARATOR);
|
||||
final StringBuilder addressBuilder = new StringBuilder();
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address
|
||||
addressBuilder.append(encodedFormattedAddress);
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code
|
||||
addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country
|
||||
return new PostalStruct(
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuffer.toString());
|
||||
reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1146,6 +1204,8 @@ public class VCardBuilder {
|
||||
}
|
||||
|
||||
public VCardBuilder appendEvents(final List<ContentValues> contentValuesList) {
|
||||
// There's possibility where a given object may have more than one birthday, which
|
||||
// is inappropriate. We just build one birthday.
|
||||
if (contentValuesList != null) {
|
||||
String primaryBirthday = null;
|
||||
String secondaryBirthday = null;
|
||||
@@ -1213,16 +1273,19 @@ public class VCardBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param emitEveryTime If true, builder builds the line even when there's no entry.
|
||||
*/
|
||||
public void appendPostalLine(final int type, final String label,
|
||||
final ContentValues contentValues,
|
||||
final boolean isPrimary, final boolean emitLineEveryTime) {
|
||||
final boolean isPrimary, final boolean emitEveryTime) {
|
||||
final boolean reallyUseQuotedPrintable;
|
||||
final boolean appendCharset;
|
||||
final String addressValue;
|
||||
{
|
||||
PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
|
||||
if (postalStruct == null) {
|
||||
if (emitLineEveryTime) {
|
||||
if (emitEveryTime) {
|
||||
reallyUseQuotedPrintable = false;
|
||||
appendCharset = false;
|
||||
addressValue = "";
|
||||
@@ -1537,7 +1600,8 @@ public class VCardBuilder {
|
||||
mBuilder.append(VCARD_END_OF_LINE);
|
||||
}
|
||||
|
||||
public void appendAndroidSpecificProperty(final String mimeType, ContentValues contentValues) {
|
||||
public void appendAndroidSpecificProperty(
|
||||
final String mimeType, ContentValues contentValues) {
|
||||
if (!sAllowedAndroidPropertySet.contains(mimeType)) {
|
||||
return;
|
||||
}
|
||||
@@ -1659,7 +1723,7 @@ public class VCardBuilder {
|
||||
encodedValue = encodeQuotedPrintable(rawValue);
|
||||
} else {
|
||||
// TODO: one line may be too huge, which may be invalid in vCard spec, though
|
||||
// several (even well-known) applications do not care this.
|
||||
// several (even well-known) applications do not care that violation.
|
||||
encodedValue = escapeCharacters(rawValue);
|
||||
}
|
||||
|
||||
@@ -1794,9 +1858,9 @@ public class VCardBuilder {
|
||||
byte[] strArray = null;
|
||||
|
||||
try {
|
||||
strArray = str.getBytes(mCharsetString);
|
||||
strArray = str.getBytes(mCharset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(LOG_TAG, "Charset " + mCharsetString + " cannot be used. "
|
||||
Log.e(LOG_TAG, "Charset " + mCharset + " cannot be used. "
|
||||
+ "Try default charset");
|
||||
strArray = str.getBytes();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.provider.ContactsContract.CommonDataKinds.Relation;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Website;
|
||||
import android.text.TextUtils;
|
||||
import android.util.CharsetUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -61,15 +62,11 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The class for composing VCard from Contacts information. Note that this is
|
||||
* completely differnt implementation from
|
||||
* android.syncml.pim.vcard.VCardComposer, which is not maintained anymore.
|
||||
* The class for composing vCard from Contacts information.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Usually, this class should be used like this.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="prettyprint">VCardComposer composer = null;
|
||||
* try {
|
||||
* composer = new VCardComposer(context);
|
||||
@@ -94,14 +91,17 @@ import java.util.Map;
|
||||
* composer.terminate();
|
||||
* }
|
||||
* } </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>
|
||||
* In default, Default {@link VCardBuilder} class is used to build each vCard.
|
||||
* </P>
|
||||
*/
|
||||
public class VCardComposer {
|
||||
private static final String LOG_TAG = "VCardComposer";
|
||||
|
||||
public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME;
|
||||
public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME;
|
||||
public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER;
|
||||
|
||||
public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
|
||||
"Failed to get database information";
|
||||
|
||||
@@ -119,6 +119,8 @@ public class VCardComposer {
|
||||
|
||||
public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
|
||||
|
||||
// Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here,
|
||||
// since usual vCard devices for Japanese devices already use it.
|
||||
private static final String SHIFT_JIS = "SHIFT_JIS";
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
|
||||
@@ -141,7 +143,7 @@ public class VCardComposer {
|
||||
sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
|
||||
sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
|
||||
sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
|
||||
// Google talk is a special case.
|
||||
// We don't add Google talk here since it has to be handled separately.
|
||||
}
|
||||
|
||||
public static interface OneEntryHandler {
|
||||
@@ -152,37 +154,37 @@ public class VCardComposer {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An useful example handler, which emits VCard String to outputstream one by one.
|
||||
* An useful handler for emitting vCard String to an OutputStream object one by one.
|
||||
* </p>
|
||||
* <p>
|
||||
* The input OutputStream object is closed() on {@link #onTerminate()}.
|
||||
* Must not close the stream outside.
|
||||
* Must not close the stream outside this class.
|
||||
* </p>
|
||||
*/
|
||||
public class HandlerForOutputStream implements OneEntryHandler {
|
||||
@SuppressWarnings("hiding")
|
||||
private static final String LOG_TAG = "vcard.VCardComposer.HandlerForOutputStream";
|
||||
|
||||
final private OutputStream mOutputStream; // mWriter will close this.
|
||||
private Writer mWriter;
|
||||
|
||||
private boolean mOnTerminateIsCalled = false;
|
||||
|
||||
final private OutputStream mOutputStream; // mWriter will close this.
|
||||
protected Writer mWriter;
|
||||
|
||||
/**
|
||||
* Input stream will be closed on the detruction of this object.
|
||||
*/
|
||||
public HandlerForOutputStream(OutputStream outputStream) {
|
||||
public HandlerForOutputStream(final OutputStream outputStream) {
|
||||
mOutputStream = outputStream;
|
||||
}
|
||||
|
||||
public boolean onInit(Context context) {
|
||||
public final boolean onInit(final Context context) {
|
||||
try {
|
||||
mWriter = new BufferedWriter(new OutputStreamWriter(
|
||||
mOutputStream, mCharsetString));
|
||||
mOutputStream, mCharset));
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.e(LOG_TAG, "Unsupported charset: " + mCharsetString);
|
||||
Log.e(LOG_TAG, "Unsupported charset: " + mCharset);
|
||||
mErrorReason = "Encoding is not supported (usually this does not happen!): "
|
||||
+ mCharsetString;
|
||||
+ mCharset;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -205,7 +207,7 @@ public class VCardComposer {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onEntryCreated(String vcard) {
|
||||
public final boolean onEntryCreated(String vcard) {
|
||||
try {
|
||||
mWriter.write(vcard);
|
||||
} catch (IOException e) {
|
||||
@@ -218,7 +220,7 @@ public class VCardComposer {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onTerminate() {
|
||||
public final void onTerminate() {
|
||||
mOnTerminateIsCalled = true;
|
||||
if (mWriter != null) {
|
||||
try {
|
||||
@@ -235,14 +237,21 @@ public class VCardComposer {
|
||||
"IOException during closing the output stream: "
|
||||
+ e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
mWriter.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
closeOutputStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Users can override this if they want to (e.g. if they don't want to close the stream).
|
||||
// TODO: Should expose bare OutputStream instead?
|
||||
public void closeOutputStream() {
|
||||
try {
|
||||
mWriter.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize() {
|
||||
if (!mOnTerminateIsCalled) {
|
||||
@@ -257,11 +266,10 @@ public class VCardComposer {
|
||||
private final ContentResolver mContentResolver;
|
||||
|
||||
private final boolean mIsDoCoMo;
|
||||
private final boolean mUsesShiftJis;
|
||||
private Cursor mCursor;
|
||||
private int mIdColumn;
|
||||
|
||||
private final String mCharsetString;
|
||||
private final String mCharset;
|
||||
private boolean mTerminateIsCalled;
|
||||
private final List<OneEntryHandler> mHandlerList;
|
||||
|
||||
@@ -272,11 +280,14 @@ public class VCardComposer {
|
||||
};
|
||||
|
||||
public VCardComposer(Context context) {
|
||||
this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
|
||||
this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The variant which sets charset to null and sets careHandlerErrors to true.
|
||||
*/
|
||||
public VCardComposer(Context context, int vcardType) {
|
||||
this(context, vcardType, true);
|
||||
this(context, vcardType, null, true);
|
||||
}
|
||||
|
||||
public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) {
|
||||
@@ -284,40 +295,88 @@ public class VCardComposer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for supporting call log entry vCard composing.
|
||||
* The variant which sets charset to null.
|
||||
*/
|
||||
public VCardComposer(final Context context, final int vcardType,
|
||||
final boolean careHandlerErrors) {
|
||||
this(context, vcardType, null, careHandlerErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for supporting call log entry vCard composing.
|
||||
*
|
||||
* @param context Context to be used during the composition.
|
||||
* @param vcardType The type of vCard, typically available via {@link VCardConfig}.
|
||||
* @param charset The charset to be used. Use null when you don't need the charset.
|
||||
* @param careHandlerErrors If true, This object returns false everytime
|
||||
* a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false.
|
||||
* If false, this ignores those errors.
|
||||
*/
|
||||
public VCardComposer(final Context context, final int vcardType,
|
||||
String charset,
|
||||
final boolean careHandlerErrors) {
|
||||
mContext = context;
|
||||
mVCardType = vcardType;
|
||||
mCareHandlerErrors = careHandlerErrors;
|
||||
mContentResolver = context.getContentResolver();
|
||||
|
||||
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
|
||||
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
|
||||
mHandlerList = new ArrayList<OneEntryHandler>();
|
||||
|
||||
if (mIsDoCoMo) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
if (mIsDoCoMo || VCardConfig.shouldUseShiftJisForExport(vcardType)) {
|
||||
if (!SHIFT_JIS.equalsIgnoreCase(charset)) {
|
||||
Log.w(LOG_TAG,
|
||||
"The charset \"" + charset + "\" is used while "
|
||||
+ SHIFT_JIS + " is needed to be used.");
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = SHIFT_JIS;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
} else {
|
||||
if (mIsDoCoMo) {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"DoCoMo-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG,
|
||||
"Career-specific SHIFT_JIS was not found. "
|
||||
+ "Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
mCharsetString = charset;
|
||||
} else if (mUsesShiftJis) {
|
||||
String charset;
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
|
||||
charset = SHIFT_JIS;
|
||||
}
|
||||
mCharsetString = charset;
|
||||
} else {
|
||||
mCharsetString = UTF_8;
|
||||
if (TextUtils.isEmpty(charset)) {
|
||||
mCharset = UTF_8;
|
||||
} else {
|
||||
try {
|
||||
charset = CharsetUtils.charsetForVendor(charset).name();
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
Log.i(LOG_TAG,
|
||||
"Career-specific \"" + charset + "\" was not found (as usual). "
|
||||
+ "Use it as is.");
|
||||
}
|
||||
mCharset = charset;
|
||||
}
|
||||
}
|
||||
Log.d(LOG_TAG, "use the charset \"" + mCharset + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +410,7 @@ public class VCardComposer {
|
||||
}
|
||||
|
||||
if (mCareHandlerErrors) {
|
||||
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||
final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||
mHandlerList.size());
|
||||
for (OneEntryHandler handler : mHandlerList) {
|
||||
if (!handler.onInit(mContext)) {
|
||||
@@ -414,7 +473,7 @@ public class VCardComposer {
|
||||
mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
|
||||
return false;
|
||||
}
|
||||
String vcard;
|
||||
final String vcard;
|
||||
try {
|
||||
if (mIdColumn >= 0) {
|
||||
vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
|
||||
@@ -437,8 +496,7 @@ public class VCardComposer {
|
||||
mCursor.moveToNext();
|
||||
}
|
||||
|
||||
// This function does not care the OutOfMemoryError on the handler side
|
||||
// :-P
|
||||
// This function does not care the OutOfMemoryError on the handler side :-P
|
||||
if (mCareHandlerErrors) {
|
||||
List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
|
||||
mHandlerList.size());
|
||||
@@ -457,7 +515,7 @@ public class VCardComposer {
|
||||
}
|
||||
|
||||
private String createOneEntryInternal(final String contactId,
|
||||
Method getEntityIteratorMethod) throws VCardException {
|
||||
final Method getEntityIteratorMethod) throws VCardException {
|
||||
final Map<String, List<ContentValues>> contentValuesListMap =
|
||||
new HashMap<String, List<ContentValues>>();
|
||||
// The resolver may return the entity iterator with no data. It is possible.
|
||||
@@ -527,20 +585,34 @@ public class VCardComposer {
|
||||
}
|
||||
}
|
||||
|
||||
final VCardBuilder builder = new VCardBuilder(mVCardType);
|
||||
builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
|
||||
.appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
|
||||
.appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
|
||||
.appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
|
||||
.appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
|
||||
.appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
|
||||
.appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE))
|
||||
.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();
|
||||
return buildVCard(contentValuesListMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in
|
||||
* {ContactsContract}. Developers can override this method to customize the output.
|
||||
*/
|
||||
public String buildVCard(final Map<String, List<ContentValues>> contentValuesListMap) {
|
||||
if (contentValuesListMap == null) {
|
||||
Log.e(LOG_TAG, "The given map is null. Ignore and return empty String");
|
||||
return "";
|
||||
} else {
|
||||
final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset);
|
||||
// TODO: Android-specific X attributes?
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
@@ -563,26 +635,38 @@ public class VCardComposer {
|
||||
@Override
|
||||
public void finalize() {
|
||||
if (!mTerminateIsCalled) {
|
||||
Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step.");
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return returns the number of available entities. The return value is undefined
|
||||
* when this object is not ready yet (typically when {{@link #init()} is not called
|
||||
* or when {@link #terminate()} is already called).
|
||||
*/
|
||||
public int getCount() {
|
||||
if (mCursor == null) {
|
||||
Log.w(LOG_TAG, "This object is not ready yet.");
|
||||
return 0;
|
||||
}
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when there's no entity to be built. The return value is undefined
|
||||
* when this object is not ready yet.
|
||||
*/
|
||||
public boolean isAfterLast() {
|
||||
if (mCursor == null) {
|
||||
Log.w(LOG_TAG, "This object is not ready yet.");
|
||||
return false;
|
||||
}
|
||||
return mCursor.isAfterLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Return the error reason if possible.
|
||||
* @return Returns the error reason.
|
||||
*/
|
||||
public String getErrorReason() {
|
||||
return mErrorReason;
|
||||
|
||||
@@ -43,10 +43,28 @@ public class VCardConfig {
|
||||
/* package */ static final int PARSE_TYPE_FOMA = 3; // For Japanese FOMA mobile phones.
|
||||
/* package */ static final int PARSE_TYPE_WINDOWS_MOBILE_JP = 4;
|
||||
|
||||
// Assumes that "iso-8859-1" is able to map "all" 8bit characters to some unicode and
|
||||
// decode the unicode to the original charset. If not, this setting will cause some bug.
|
||||
public static final String DEFAULT_CHARSET = "iso-8859-1";
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* The charset used during import.
|
||||
* </P>
|
||||
* <P>
|
||||
* We cannot determine which charset should be used to interpret a given vCard file,
|
||||
* while we have to decode sime encoded data (e.g. BASE64) to binary.
|
||||
* In order to avoid "misinterpretation" of charset as much as possible,
|
||||
* "ISO-8859-1" (a.k.a Latin-1) is first used for reading a stream.
|
||||
* When charset is specified in a property (with "CHARSET=..." parameter),
|
||||
* the string is decoded to raw bytes and encoded into the specific charset,
|
||||
* assuming "ISO-8859-1" is able to map "all" 8bit characters to some unicode,
|
||||
* and it has 1 to 1 mapping in all 8bit characters.
|
||||
* If the assumption is not correct, this setting will cause some bug.
|
||||
* </P>
|
||||
*/
|
||||
/* package */ static final String DEFAULT_TEMPORARY_CHARSET = "ISO-8859-1";
|
||||
|
||||
// TODO: still intermediate procedures uses this charset. Fix it.
|
||||
public static final String DEFAULT_IMPORT_CHARSET = "ISO-8859-1";
|
||||
public static final String DEFAULT_EXPORT_CHARSET = "UTF-8";
|
||||
|
||||
public static final int FLAG_V21 = 0;
|
||||
public static final int FLAG_V30 = 1;
|
||||
|
||||
@@ -58,10 +76,13 @@ public class VCardConfig {
|
||||
private static final int NAME_ORDER_MASK = 0xC;
|
||||
|
||||
// 0x10 is reserved for safety
|
||||
|
||||
private static final int FLAG_CHARSET_UTF8 = 0;
|
||||
private static final int FLAG_CHARSET_SHIFT_JIS = 0x100;
|
||||
private static final int FLAG_CHARSET_MASK = 0xF00;
|
||||
|
||||
/*
|
||||
* These flags are ignored when charset is explicitly given by a caller.
|
||||
*/
|
||||
private static final int FLAG_USE_UTF8_FOR_EXPORT = 0;
|
||||
private static final int FLAG_USE_SHIFT_JIS_FOR_EXPORT = 0x100;
|
||||
private static final int FLAG_CHARSET_MASK_FOR_EKPORT = 0xF00;
|
||||
|
||||
/**
|
||||
* The flag indicating the vCard composer will add some "X-" properties used only in Android
|
||||
@@ -196,7 +217,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V21_GENERIC_UTF8 =
|
||||
(FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
|
||||
(FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_USE_UTF8_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static String VCARD_TYPE_V21_GENERIC_UTF8_STR = "v21_generic";
|
||||
@@ -210,7 +231,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V30_GENERIC_UTF8 =
|
||||
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
|
||||
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_USE_UTF8_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V30_GENERIC_UTF8_STR = "v30_generic";
|
||||
@@ -222,7 +243,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V21_EUROPE_UTF8 =
|
||||
(FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
|
||||
(FLAG_V21 | NAME_ORDER_EUROPE | FLAG_USE_UTF8_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V21_EUROPE_UTF8_STR = "v21_europe";
|
||||
@@ -236,7 +257,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V30_EUROPE_UTF8 =
|
||||
(FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
|
||||
(FLAG_V30 | NAME_ORDER_EUROPE | FLAG_USE_UTF8_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
|
||||
@@ -250,7 +271,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V21_JAPANESE_UTF8 =
|
||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
|
||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_UTF8_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8";
|
||||
@@ -265,7 +286,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V21_JAPANESE_SJIS =
|
||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
|
||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_SHIFT_JIS_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V21_JAPANESE_SJIS_STR = "v21_japanese_sjis";
|
||||
@@ -280,7 +301,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V30_JAPANESE_SJIS =
|
||||
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
|
||||
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_USE_SHIFT_JIS_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V30_JAPANESE_SJIS_STR = "v30_japanese_sjis";
|
||||
@@ -294,7 +315,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
|
||||
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
|
||||
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_USE_UTF8_FOR_EXPORT |
|
||||
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
|
||||
|
||||
/* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8";
|
||||
@@ -310,7 +331,7 @@ public class VCardConfig {
|
||||
* </P>
|
||||
*/
|
||||
public static final int VCARD_TYPE_V21_JAPANESE_MOBILE =
|
||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
|
||||
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_SHIFT_JIS_FOR_EXPORT |
|
||||
FLAG_CONVERT_PHONETIC_NAME_STRINGS |
|
||||
FLAG_REFRAIN_QP_TO_NAME_PROPERTIES);
|
||||
|
||||
@@ -379,12 +400,17 @@ public class VCardConfig {
|
||||
return !isV30(vcardType);
|
||||
}
|
||||
|
||||
public static boolean usesUtf8(final int vcardType) {
|
||||
return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_UTF8);
|
||||
/* package */ static boolean shouldUseUtf8ForExport(final int vcardType) {
|
||||
return ((vcardType & FLAG_CHARSET_MASK_FOR_EKPORT) == FLAG_USE_UTF8_FOR_EXPORT);
|
||||
}
|
||||
|
||||
public static boolean usesShiftJis(final int vcardType) {
|
||||
return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_SHIFT_JIS);
|
||||
/**
|
||||
* Shift_JIS (a charset for Japanese text files) needs special handling to select
|
||||
* carrer specific variants.
|
||||
* @hide just for test
|
||||
*/
|
||||
public static boolean shouldUseShiftJisForExport(final int vcardType) {
|
||||
return ((vcardType & FLAG_CHARSET_MASK_FOR_EKPORT) == FLAG_USE_SHIFT_JIS_FOR_EXPORT);
|
||||
}
|
||||
|
||||
public static int getNameOrderType(final int vcardType) {
|
||||
|
||||
@@ -80,7 +80,7 @@ public class VCardEntryConstructor implements VCardInterpreter {
|
||||
if (inputCharset != null) {
|
||||
mInputCharset = inputCharset;
|
||||
} else {
|
||||
mInputCharset = VCardConfig.DEFAULT_CHARSET;
|
||||
mInputCharset = VCardConfig.DEFAULT_TEMPORARY_CHARSET;
|
||||
}
|
||||
if (charsetForDetodedBytes != null) {
|
||||
mCharsetForDecodedBytes = charsetForDetodedBytes;
|
||||
|
||||
@@ -34,26 +34,17 @@ public abstract class VCardParser {
|
||||
|
||||
/**
|
||||
* <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>
|
||||
* Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
|
||||
* local encoding to it. For example, Japanese phone career uses Shift_JIS, which is
|
||||
* formally allowed in VCard 2.1, but not recommended in VCard 3.0. In VCard 2.1,
|
||||
* In some exreme case, some VCard may have different charsets in one VCard (though
|
||||
* we do not see any device which emits such kind of malicious data)
|
||||
* formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1,
|
||||
* In some exreme case, it is allowed for vCard to have different charsets in one vCard.
|
||||
* </P>
|
||||
* <P>
|
||||
* In order to avoid "misunderstanding" charset as much as possible, this method
|
||||
* use "ISO-8859-1" for reading the stream. When charset is specified in some property
|
||||
* (with "CHARSET=..." parameter), the string is decoded to raw bytes and encoded to
|
||||
* the charset. This method assumes that "ISO-8859-1" has 1 to 1 mapping in all 8bit
|
||||
* characters, which is not completely sure. In some cases, this "decoding-encoding"
|
||||
* scheme may fail. To avoid the case,
|
||||
* </P>
|
||||
* <P>
|
||||
* We recommend you to use {@link VCardSourceDetector} and detect which kind of source the
|
||||
* VCard comes from and explicitly specify a charset using the result.
|
||||
* We recommend you use {@link VCardSourceDetector} and detect which kind of source the
|
||||
* vCard comes from and explicitly specify a charset using the result.
|
||||
* </P>
|
||||
*
|
||||
* @param is The source to parse.
|
||||
@@ -61,27 +52,24 @@ public abstract class VCardParser {
|
||||
* @return Returns true for success. Otherwise returns false.
|
||||
* @throws IOException, VCardException
|
||||
*/
|
||||
public abstract boolean parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException;
|
||||
|
||||
public final boolean parse(InputStream is, VCardInterpreter interepreter)
|
||||
throws IOException, VCardException {
|
||||
return parse(is, VCardConfig.DEFAULT_TEMPORARY_CHARSET, interepreter);
|
||||
}
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* The method variants which accept charset.
|
||||
* </P>
|
||||
* <P>
|
||||
* RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use
|
||||
* UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese
|
||||
* phone uses Shift_JIS as a charset (e.g. W61SH), and another uses
|
||||
* "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification (e.g. W53K).
|
||||
* </P>
|
||||
*
|
||||
* @param is The source to parse.
|
||||
* @param charset Charset to be used.
|
||||
* @param builder The VCardBuilderBase object.
|
||||
* @param interpreter 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 boolean parse(InputStream is, String charset,
|
||||
VCardInterpreter interpreter)
|
||||
throws IOException, VCardException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,7 +68,28 @@ public class VCardParser_V21 extends VCardParser {
|
||||
private static final HashSet<String> sAvailableEncodingV21 =
|
||||
new HashSet<String>(Arrays.asList(
|
||||
"7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
|
||||
|
||||
|
||||
private static final 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Used only for parsing END:VCARD.
|
||||
private String mPreviousLine;
|
||||
|
||||
@@ -839,19 +860,17 @@ public class VCardParser_V21 extends VCardParser {
|
||||
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;
|
||||
if (is == null) {
|
||||
throw new NullPointerException("InputStream must not be null.");
|
||||
}
|
||||
if (charset == null) {
|
||||
charset = VCardConfig.DEFAULT_TEMPORARY_CHARSET;
|
||||
}
|
||||
|
||||
final InputStreamReader tmpReader = new InputStreamReader(is, charset);
|
||||
if (VCardConfig.showPerformanceLog()) {
|
||||
mReader = new CustomBufferedReader(tmpReader);
|
||||
@@ -913,24 +932,3 @@ public class VCardParser_V21 extends VCardParser {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,13 @@
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Nickname;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Note;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
||||
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
||||
|
||||
import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class VCardJapanizationTests extends VCardTestsBase {
|
||||
@@ -114,7 +112,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
|
||||
|
||||
final ContentValues contentValues =
|
||||
(VCardConfig.usesShiftJis(vcardType) ?
|
||||
(VCardConfig.shouldUseShiftJisForExport(vcardType) ?
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
||||
mContentValuesForQPAndSJis) :
|
||||
(VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8));
|
||||
@@ -214,7 +212,7 @@ public class VCardJapanizationTests extends VCardTestsBase {
|
||||
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
|
||||
.put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A");
|
||||
|
||||
ContentValues contentValues = (VCardConfig.usesShiftJis(vcardType) ?
|
||||
ContentValues contentValues = (VCardConfig.shouldUseShiftJisForExport(vcardType) ?
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
|
||||
mContentValuesForQPAndSJis) :
|
||||
(VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 :
|
||||
|
||||
@@ -214,7 +214,7 @@ import java.util.Arrays;
|
||||
InputStream is = null;
|
||||
try {
|
||||
String charset =
|
||||
(VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8");
|
||||
(VCardConfig.shouldUseShiftJisForExport(mVCardType) ? "SHIFT_JIS" : "UTF-8");
|
||||
is = new ByteArrayInputStream(vcard.getBytes(charset));
|
||||
mTestCase.assertEquals(true, parser.parse(is, null, builder));
|
||||
} catch (IOException e) {
|
||||
@@ -292,8 +292,7 @@ import java.util.Arrays;
|
||||
while (!composer.isAfterLast()) {
|
||||
try {
|
||||
final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
|
||||
mTestCase.assertTrue(
|
||||
composer.createOneEntry(getMockGetEntityIteratorMethod()));
|
||||
mTestCase.assertTrue(composer.createOneEntry(getMockGetEntityIteratorMethod()));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
mTestCase.fail();
|
||||
|
||||
@@ -68,7 +68,7 @@ public class VNodeBuilder implements VCardInterpreter {
|
||||
private boolean mStrictLineBreakParsing;
|
||||
|
||||
public VNodeBuilder() {
|
||||
this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false);
|
||||
this(VCardConfig.DEFAULT_IMPORT_CHARSET, TARGET_CHARSET, false);
|
||||
}
|
||||
|
||||
public VNodeBuilder(String charset, boolean strictLineBreakParsing) {
|
||||
@@ -83,7 +83,7 @@ public class VNodeBuilder implements VCardInterpreter {
|
||||
if (sourceCharset != null) {
|
||||
mSourceCharset = sourceCharset;
|
||||
} else {
|
||||
mSourceCharset = VCardConfig.DEFAULT_CHARSET;
|
||||
mSourceCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
|
||||
}
|
||||
if (targetCharset != null) {
|
||||
mTargetCharset = targetCharset;
|
||||
|
||||
Reference in New Issue
Block a user