Merge change 22399 into eclair

* changes:
  Refactor VCard handling code, phase 2, 3, 4, 5
This commit is contained in:
Android (Google) Code Review
2009-08-25 22:08:38 -07:00
14 changed files with 3413 additions and 978 deletions

View File

@@ -0,0 +1,94 @@
/*
* 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;
/**
* Constants used in both composer and parser.
*/
/* package */ class Constants {
public static final String ATTR_TYPE = "TYPE";
public static final String VERSION_V21 = "2.1";
public static final String VERSION_V30 = "3.0";
// Properties both the current (as of 2009-08-17) ContactsStruct and de-fact vCard extensions
// shown in http://en.wikipedia.org/wiki/VCard support are defined here.
public static final String PROPERTY_X_AIM = "X-AIM";
public static final String PROPERTY_X_MSN = "X-MSN";
public static final String PROPERTY_X_YAHOO = "X-YAHOO";
public static final String PROPERTY_X_ICQ = "X-ICQ";
public static final String PROPERTY_X_JABBER = "X-JABBER";
public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK";
public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME";
// Phone number for Skype, available as usual phone.
public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
// Some device emits this "X-" attribute, which is specifically invalid but should be
// always properly accepted, and emitted in some special case (for that device/application).
public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
// How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0
//
// e.g.
// 1) Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."
// 2) Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."
// 3) Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."
//
// 2) has been the default of VCard exporter/importer in Android, but we can see the other
// formats in vCard data emitted by the other softwares/devices.
//
// So we are currently not sure which type is the best; probably we will have to change which
// type should be emitted depending on the device.
public static final String ATTR_TYPE_HOME = "HOME";
public static final String ATTR_TYPE_WORK = "WORK";
public static final String ATTR_TYPE_FAX = "FAX";
public static final String ATTR_TYPE_CELL = "CELL";
public static final String ATTR_TYPE_VOICE = "VOICE";
public static final String ATTR_TYPE_INTERNET = "INTERNET";
public static final String ATTR_TYPE_PREF = "PREF";
// Phone types valid in vCard and known to ContactsContract, but not so common.
public static final String ATTR_TYPE_CAR = "CAR";
public static final String ATTR_TYPE_ISDN = "ISDN";
public static final String ATTR_TYPE_PAGER = "PAGER";
// Phone types existing in vCard 2.1 but not known to ContactsContract.
// TODO: should make parser make these TYPE_CUSTOM.
public static final String ATTR_TYPE_MODEM = "MODEM";
public static final String ATTR_TYPE_MSG = "MSG";
public static final String ATTR_TYPE_BBS = "BBS";
public static final String ATTR_TYPE_VIDEO = "VIDEO";
// Phone types existing in the current Contacts structure but not valid in vCard (at least 2.1)
// These types are encoded to "X-" attributes when composing vCard for now.
// Parser passes these even if "X-" is added to the attribute.
public static final String ATTR_TYPE_PHONE_EXTRA_OTHER = "OTHER";
public static final String ATTR_TYPE_PHONE_EXTRA_CALLBACK = "CALLBACK";
// TODO: may be "TYPE=COMPANY,PREF", not "COMPANY-MAIN".
public static final String ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN = "COMPANY-MAIN";
public static final String ATTR_TYPE_PHONE_EXTRA_RADIO = "RADIO";
public static final String ATTR_TYPE_PHONE_EXTRA_TELEX = "TELEX";
public static final String ATTR_TYPE_PHONE_EXTRA_TTY_TDD = "TTY-TDD";
public static final String ATTR_TYPE_PHONE_EXTRA_ASSISTANT = "ASSISTANT";
// DoCoMo specific attribute. Used with "SOUND" property, which is alternate of SORT-STRING in
// vCard 3.0.
public static final String ATTR_TYPE_X_IRMC_N = "X-IRMC-N";
private Constants() {
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,11 +15,7 @@
*/
package android.pim.vcard;
import android.content.AbstractSyncableContentProvider;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.IContentProvider;
import android.provider.Contacts;
import android.util.Log;
/**
@@ -27,62 +23,26 @@ import android.util.Log;
*/
public class EntryCommitter implements EntryHandler {
public static String LOG_TAG = "vcard.EntryComitter";
private ContentResolver mContentResolver;
// Ideally, this should be ContactsProvider but it seems Class loader cannot find it,
// even when it is subclass of ContactsProvider...
private AbstractSyncableContentProvider mProvider;
private long mMyContactsGroupId;
private long mTimeToCommit;
public EntryCommitter(ContentResolver resolver) {
mContentResolver = resolver;
tryGetOriginalProvider();
}
public void onFinal() {
if (VCardConfig.showPerformanceLog()) {
Log.d(LOG_TAG,
String.format("time to commit entries: %ld ms", mTimeToCommit));
}
}
private void tryGetOriginalProvider() {
final ContentResolver resolver = mContentResolver;
if ((mMyContactsGroupId = Contacts.People.tryGetMyContactsGroupId(resolver)) == 0) {
Log.e(LOG_TAG, "Could not get group id of MyContact");
return;
}
IContentProvider iProviderForName = resolver.acquireProvider(Contacts.CONTENT_URI);
ContentProvider contentProvider =
ContentProvider.coerceToLocalContentProvider(iProviderForName);
if (contentProvider == null) {
Log.e(LOG_TAG, "Fail to get ContentProvider object.");
return;
}
if (!(contentProvider instanceof AbstractSyncableContentProvider)) {
Log.e(LOG_TAG,
"Acquired ContentProvider object is not AbstractSyncableContentProvider.");
return;
}
mProvider = (AbstractSyncableContentProvider)contentProvider;
public void onParsingStart() {
}
public void onParsingEnd() {
if (VCardConfig.showPerformanceLog()) {
Log.d(LOG_TAG, String.format("time to commit entries: %d ms", mTimeToCommit));
}
}
public void onEntryCreated(final ContactStruct contactStruct) {
long start = System.currentTimeMillis();
if (mProvider != null) {
contactStruct.pushIntoAbstractSyncableContentProvider(
mProvider, mMyContactsGroupId);
} else {
contactStruct.pushIntoContentResolver(mContentResolver);
}
contactStruct.pushIntoContentResolver(mContentResolver);
mTimeToCommit += System.currentTimeMillis() - start;
}
}

View File

@@ -16,18 +16,23 @@
package android.pim.vcard;
/**
* Unlike VCardBuilderBase, this (and VCardDataBuilder) assumes
* Unlike {@link VCardBuilder}, this (and {@link VCardDataBuilder}) assumes
* "each VCard entry should be correctly parsed and passed to each EntryHandler object",
*/
public interface EntryHandler {
/**
* Able to be use this method for showing performance log, etc.
* TODO: better name?
* Called when the parsing started.
*/
public void onFinal();
public void onParsingStart();
/**
* The method called when one VCard entry is successfully created
*/
public void onEntryCreated(final ContactStruct entry);
/**
* Called when the parsing ended.
* Able to be use this method for showing performance log, etc.
*/
public void onParsingEnd();
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,43 +15,267 @@
*/
package android.pim.vcard;
import java.util.HashMap;
import java.util.Map;
/**
* The class representing VCard related configurations
* The class representing VCard related configurations. Useful static methods are not in this class
* but in VCardUtils.
*/
public class VCardConfig {
static final int LOG_LEVEL_NONE = 0;
static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1;
static final int LOG_LEVEL_SHOW_WARNING = 0x2;
static final int LOG_LEVEL_VERBOSE =
// TODO: may be better to make the instance of this available and stop using static methods and
// one integer.
/* package */ static final int LOG_LEVEL_NONE = 0;
/* package */ static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1;
/* package */ static final int LOG_LEVEL_SHOW_WARNING = 0x2;
/* package */ static final int LOG_LEVEL_VERBOSE =
LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING;
/* package */ static final int LOG_LEVEL = LOG_LEVEL_PERFORMANCE_MEASUREMENT;
// 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";
// TODO: use this flag
public static boolean IGNORE_CASE_EXCEPT_VALUE = true;
// TODO: make the other codes use this flag
public static final boolean IGNORE_CASE_EXCEPT_VALUE = true;
protected static final int LOG_LEVEL = LOG_LEVEL_PERFORMANCE_MEASUREMENT;
private static final int FLAG_V21 = 0;
private static final int FLAG_V30 = 1;
// 0x2 is reserved for the future use ...
public static final int NAME_ORDER_DEFAULT = 0;
public static final int NAME_ORDER_EUROPE = 0x4;
public static final int NAME_ORDER_JAPANESE = 0x8;
private static final int NAME_ORDER_MASK = 0xC;
// 0x10 is reserved for safety
// Note: phonetic name probably should be "LAST FIRST MIDDLE" for European languages, and
// space should be added between each element while it should not be in Japanese.
// But unfortunately, we currently do not have the data and are not sure whether we should
// support European version of name ordering.
//
// TODO: Implement the logic described above if we really need European version of
// phonetic name handling. Also, adding the appropriate test case of vCard would be
// highly appreciated.
public static final int NAME_ORDER_TYPE_ENGLISH = 0;
public static final int NAME_ORDER_TYPE_JAPANESE = 1;
public static final int NAME_ORDER_TYPE_DEFAULT = NAME_ORDER_TYPE_ENGLISH;
private static final int FLAG_CHARSET_UTF8 = 0;
private static final int FLAG_CHARSET_SHIFT_JIS = 0x20;
/**
* @hide temporal. may be deleted
* 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.
*
* For example, Android accepts nickname information while vCard 2.1 does not.
* When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME")
* instead of just dropping it.
*
* vCard parser code automatically parses the field emitted even when this flag is off.
*
* Note that this flag does not assure all the information must be hold in the emitted vCard.
*/
private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000;
/**
* The flag indicating the vCard composer will add some "X-" properties seen in the
* vCard data emitted by the other softwares/devices when the formal vCard specification
* does not have appropriate field(s) for that data.
*
* One example is X-PHONETIC-FIRST-NAME/X-PHONETIC-MIDDLE-NAME/X-PHONETIC-LAST-NAME, which are
* for phonetic name (how the name is pronounced), seen in the vCard emitted by some other
* non-Android devices/softwares. We chose to enable the vCard composer to use those
* defact properties since they are also useful for Android devices.
*
* Note for developers: only "X-" properties should be added with this flag. vCard 2.1/3.0
* allows any kind of "X-" properties but does not allow non-"X-" properties (except IANA tokens
* in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties.
*/
private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000;
/**
* The flag indicating some specific dialect seen in vcard of DoCoMo (one of Japanese
* mobile careers) should be used. This flag does not include any other information like
* that "the vCard is for Japanese". So it is "possible" that "the vCard should have DoCoMo's
* dialect but the name order should be European", but it is not recommended.
*/
private static final int FLAG_DOCOMO = 0x20000000;
// VCard types
/**
* General vCard format with the version 2.1. Uses UTF-8 for the charset.
* When composing a vCard entry, the US convension will be used.
*
* e.g. The order of the display name would be "Prefix Given Middle Family Suffix",
* while in Japan, it should be "Prefix Family Middle Given Suffix".
*/
public static final int VCARD_TYPE_V21_GENERIC =
(FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
/**
* General vCard format with the version 3.0. Uses UTF-8 for the charset.
*
* Note that this type is not fully implemented, so probably some bugs remain especially
* in parsing part.
*
* TODO: implement this type.
*/
public static final int VCARD_TYPE_V30_GENERIC =
(FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic";
/**
* General vCard format with the version 2.1 with some Europe convension. Uses Utf-8.
* Currently, only name order is considered ("Prefix Middle Given Family Suffix")
*/
public static final int VCARD_TYPE_V21_EUROPE =
(FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
/**
* General vCard format with the version 3.0 with some Europe convension. Uses UTF-8
*/
public static final int VCARD_TYPE_V30_EUROPE =
(FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
/**
* vCard 2.1 format for miscellaneous Japanese devices. Shift_Jis is used for
* parsing/composing the vCard data.
*/
public static final int VCARD_TYPE_V21_JAPANESE =
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese";
/**
* vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
*/
public static final int VCARD_TYPE_V21_JAPANESE_UTF8 =
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8";
/**
* vCard format for miscellaneous Japanese devices, using Shift_Jis for
* parsing/composing the vCard data.
*/
public static final int VCARD_TYPE_V30_JAPANESE =
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese";
/**
* vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
*/
public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
(FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
/* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8";
/**
* VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
* Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
* No Android-specific property nor defact property is included.
*/
public static final int VCARD_TYPE_DOCOMO =
(FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS | FLAG_DOCOMO);
private static final String VCARD_TYPE_DOCOMO_STR = "docomo";
public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC;
private static final Map<String, Integer> VCARD_TYPES_MAP;
static {
VCARD_TYPES_MAP = new HashMap<String, Integer>();
VCARD_TYPES_MAP.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
VCARD_TYPES_MAP.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
VCARD_TYPES_MAP.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
VCARD_TYPES_MAP.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
VCARD_TYPES_MAP.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
}
public static int getVCardTypeFromString(String vcardTypeString) {
String loweredKey = vcardTypeString.toLowerCase();
if (VCARD_TYPES_MAP.containsKey(loweredKey)) {
return VCARD_TYPES_MAP.get(loweredKey);
} else {
// XXX: should return the value indicating the input is invalid?
return VCARD_TYPE_DEFAULT;
}
}
public static boolean isV30(int vcardType) {
return ((vcardType & FLAG_V30) != 0);
}
public static boolean usesQuotedPrintable(int vcardType) {
return !isV30(vcardType);
}
public static boolean isDoCoMo(int vcardType) {
return ((vcardType & FLAG_DOCOMO) != 0);
}
/**
* @return true if the device is Japanese and some Japanese convension is
* applied to creating "formatted" something like FORMATTED_ADDRESS.
*/
public static boolean isJapaneseDevice(int vcardType) {
return ((vcardType == VCARD_TYPE_V21_JAPANESE) ||
(vcardType == VCARD_TYPE_V21_JAPANESE_UTF8) ||
(vcardType == VCARD_TYPE_V30_JAPANESE) ||
(vcardType == VCARD_TYPE_V30_JAPANESE_UTF8) ||
(vcardType == VCARD_TYPE_DOCOMO));
}
public static boolean usesShiftJis(int vcardType) {
return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0);
}
/**
* @return true when Japanese phonetic string must be converted to a string
* containing only half-width katakana. This method exists since Japanese mobile
* phones usually use only half-width katakana for expressing phonetic names and
* some devices are not ready for parsing other phonetic strings like hiragana and
* full-width katakana.
*/
public static boolean needsToConvertPhoneticString(int vcardType) {
return (vcardType == VCARD_TYPE_DOCOMO);
}
public static int getNameOrderType(int vcardType) {
return vcardType & NAME_ORDER_MASK;
}
public static boolean usesAndroidSpecificProperty(int vcardType) {
return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0);
}
public static boolean usesDefactProperty(int vcardType) {
return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0);
}
public static boolean onlyOneNoteFieldIsAvailable(int vcardType) {
return vcardType == VCARD_TYPE_DOCOMO;
}
public static boolean showPerformanceLog() {
return (LOG_LEVEL & LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
}
private VCardConfig() {

View File

@@ -59,7 +59,7 @@ public class VCardDataBuilder implements VCardBuilder {
private String mTargetCharset;
private boolean mStrictLineBreakParsing;
private int mNameOrderType;
private int mVCardType;
// Just for testing.
private long mTimePushIntoContentResolver;
@@ -67,23 +67,21 @@ public class VCardDataBuilder implements VCardBuilder {
private List<EntryHandler> mEntryHandlers = new ArrayList<EntryHandler>();
public VCardDataBuilder() {
this(null, null, false, VCardConfig.NAME_ORDER_TYPE_DEFAULT);
this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC);
}
/**
* @hide
*/
public VCardDataBuilder(int nameOrderType) {
this(null, null, false, nameOrderType);
public VCardDataBuilder(int vcardType) {
this(null, null, false, vcardType);
}
/**
* @hide
*/
public VCardDataBuilder(String charset,
boolean strictLineBreakParsing,
int nameOrderType) {
this(null, charset, strictLineBreakParsing, nameOrderType);
public VCardDataBuilder(String charset, boolean strictLineBreakParsing, int vcardType) {
this(null, charset, strictLineBreakParsing, vcardType);
}
/**
@@ -92,7 +90,7 @@ public class VCardDataBuilder implements VCardBuilder {
public VCardDataBuilder(String sourceCharset,
String targetCharset,
boolean strictLineBreakParsing,
int nameOrderType) {
int vcardType) {
if (sourceCharset != null) {
mSourceCharset = sourceCharset;
} else {
@@ -104,7 +102,7 @@ public class VCardDataBuilder implements VCardBuilder {
mTargetCharset = TARGET_CHARSET;
}
mStrictLineBreakParsing = strictLineBreakParsing;
mNameOrderType = nameOrderType;
mVCardType = vcardType;
}
public void addEntryHandler(EntryHandler entryHandler) {
@@ -112,11 +110,14 @@ public class VCardDataBuilder implements VCardBuilder {
}
public void start() {
for (EntryHandler entryHandler : mEntryHandlers) {
entryHandler.onParsingStart();
}
}
public void end() {
for (EntryHandler entryHandler : mEntryHandlers) {
entryHandler.onFinal();
entryHandler.onParsingEnd();
}
}
@@ -135,7 +136,7 @@ public class VCardDataBuilder implements VCardBuilder {
Log.e(LOG_TAG, "This is not VCARD!");
}
mCurrentContactStruct = new ContactStruct(mNameOrderType);
mCurrentContactStruct = new ContactStruct(mVCardType);
}
public void endRecord() {
@@ -164,8 +165,7 @@ public class VCardDataBuilder implements VCardBuilder {
public void propertyParamType(String type) {
if (mParamType != null) {
Log.e(LOG_TAG,
"propertyParamType() is called more than once " +
Log.e(LOG_TAG, "propertyParamType() is called more than once " +
"before propertyParamValue() is called");
}
mParamType = type;
@@ -173,6 +173,7 @@ public class VCardDataBuilder implements VCardBuilder {
public void propertyParamValue(String value) {
if (mParamType == null) {
// From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
mParamType = "TYPE";
}
mCurrentProperty.addParameter(mParamType, value);
@@ -297,7 +298,7 @@ public class VCardDataBuilder implements VCardBuilder {
String charset =
((charsetCollection != null) ? charsetCollection.iterator().next() : null);
String targetCharset = CharsetUtils.nameForDefaultVendor(charset);
final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING");
String encoding =
((encodingCollection != null) ? encodingCollection.iterator().next() : null);

View File

@@ -34,7 +34,7 @@ import java.util.HashSet;
* This class is used to parse vcard. Please refer to vCard Specification 2.1.
*/
public class VCardParser_V21 extends VCardParser {
private static final String LOG_TAG = "VCardParser_V21";
private static final String LOG_TAG = "vcard.VCardParser_V21";
/** Store the known-type */
private static final HashSet<String> sKnownTypeSet = new HashSet<String>(
@@ -58,8 +58,10 @@ public class VCardParser_V21 extends VCardParser {
"VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
"BDAY", "ROLE", "REV", "UID", "KEY", "MAILER"));
// Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
// We allow it for safety...
/**
* Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
* We allow it for safety...
*/
private static final HashSet<String> sAvailableEncodingV21 =
new HashSet<String>(Arrays.asList(
"7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
@@ -70,7 +72,10 @@ public class VCardParser_V21 extends VCardParser {
/** The builder to build parsed data */
protected VCardBuilder mBuilder = null;
/** The encoding type */
/**
* 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";
@@ -88,17 +93,17 @@ public class VCardParser_V21 extends VCardParser {
// Just for debugging
private long mTimeTotal;
private long mTimeStartRecord;
private long mTimeEndRecord;
private long mTimeReadStartRecord;
private long mTimeReadEndRecord;
private long mTimeStartProperty;
private long mTimeEndProperty;
private long mTimeParseItems;
private long mTimeParseItem1;
private long mTimeParseItem2;
private long mTimeParseItem3;
private long mTimeHandlePropertyValue1;
private long mTimeHandlePropertyValue2;
private long mTimeHandlePropertyValue3;
private long mTimeParseLineAndHandleGroup;
private long mTimeParsePropertyValues;
private long mTimeParseAdrOrgN;
private long mTimeHandleMiscPropertyValue;
private long mTimeHandleQuotedPrintable;
private long mTimeHandleBase64;
/**
* Create a new VCard parser.
@@ -213,7 +218,7 @@ public class VCardParser_V21 extends VCardParser {
if (mBuilder != null) {
start = System.currentTimeMillis();
mBuilder.startRecord("VCARD");
mTimeStartRecord += System.currentTimeMillis() - start;
mTimeReadStartRecord += System.currentTimeMillis() - start;
}
start = System.currentTimeMillis();
parseItems();
@@ -222,7 +227,7 @@ public class VCardParser_V21 extends VCardParser {
if (mBuilder != null) {
start = System.currentTimeMillis();
mBuilder.endRecord();
mTimeEndRecord += System.currentTimeMillis() - start;
mTimeReadEndRecord += System.currentTimeMillis() - start;
}
return true;
}
@@ -250,26 +255,6 @@ public class VCardParser_V21 extends VCardParser {
// Though vCard 2.1/3.0 specification does not allow lower cases,
// some data may have them, so we allow it (Actually, previous code
// had explicitly allowed "BEGIN:vCard" though there's no example).
//
// TODO: ignore non vCard entry (e.g. vcalendar).
// XXX: Not sure, but according to VDataBuilder.java, vcalendar
// entry
// may be nested. Just seeking "END:SOMETHING" may not be enough.
// e.g.
// BEGIN:VCARD
// ... (Valid. Must parse this)
// END:VCARD
// BEGIN:VSOMETHING
// ... (Must ignore this)
// BEGIN:VSOMETHING2
// ... (Must ignore this)
// END:VSOMETHING2
// ... (Must ignore this!)
// END:VSOMETHING
// BEGIN:VCARD
// ... (Valid. Must parse this)
// END:VCARD
// INVALID_STRING (VCardException should be thrown)
if (length == 2 &&
strArray[0].trim().equalsIgnoreCase("BEGIN") &&
strArray[1].trim().equalsIgnoreCase("VCARD")) {
@@ -367,11 +352,11 @@ public class VCardParser_V21 extends VCardParser {
}
/**
* 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
* 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;
@@ -389,14 +374,13 @@ public class VCardParser_V21 extends VCardParser {
String propertyName = propertyNameAndValue[0].toUpperCase();
String propertyValue = propertyNameAndValue[1];
mTimeParseItem1 += System.currentTimeMillis() - start;
mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start;
if (propertyName.equals("ADR") ||
propertyName.equals("ORG") ||
if (propertyName.equals("ADR") || propertyName.equals("ORG") ||
propertyName.equals("N")) {
start = System.currentTimeMillis();
handleMultiplePropertyValue(propertyName, propertyValue);
mTimeParseItem3 += System.currentTimeMillis() - start;
mTimeParseAdrOrgN += System.currentTimeMillis() - start;
return false;
} else if (propertyName.equals("AGENT")) {
handleAgent(propertyValue);
@@ -408,14 +392,13 @@ public class VCardParser_V21 extends VCardParser {
} else {
throw new VCardException("Unknown BEGIN type: " + propertyValue);
}
} else if (propertyName.equals("VERSION") &&
!propertyValue.equals(getVersion())) {
} else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersion())) {
throw new VCardVersionException("Incompatible version: " +
propertyValue + " != " + getVersion());
}
start = System.currentTimeMillis();
handlePropertyValue(propertyName, propertyValue);
mTimeParseItem2 += System.currentTimeMillis() - start;
mTimeParsePropertyValues += System.currentTimeMillis() - start;
return false;
}
@@ -542,7 +525,7 @@ public class VCardParser_V21 extends VCardParser {
}
/**
* ptypeval = knowntype / "X-" word
* ptypeval = knowntype / "X-" word
*/
protected void handleType(String ptypeval) {
String upperTypeValue = ptypeval;
@@ -637,8 +620,7 @@ public class VCardParser_V21 extends VCardParser {
}
}
protected void handlePropertyValue(
String propertyName, String propertyValue) throws
protected void handlePropertyValue(String propertyName, String propertyValue) throws
IOException, VCardException {
if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
long start = System.currentTimeMillis();
@@ -648,7 +630,7 @@ public class VCardParser_V21 extends VCardParser {
v.add(result);
mBuilder.propertyValues(v);
}
mTimeHandlePropertyValue2 += System.currentTimeMillis() - start;
mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
} else if (mEncoding.equalsIgnoreCase("BASE64") ||
mEncoding.equalsIgnoreCase("B")) {
long start = System.currentTimeMillis();
@@ -667,7 +649,7 @@ public class VCardParser_V21 extends VCardParser {
mBuilder.propertyValues(null);
}
}
mTimeHandlePropertyValue3 += System.currentTimeMillis() - start;
mTimeHandleBase64 += System.currentTimeMillis() - start;
} else {
if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT")
|| mEncoding.equalsIgnoreCase("8BIT")
@@ -681,7 +663,7 @@ public class VCardParser_V21 extends VCardParser {
v.add(maybeUnescapeText(propertyValue));
mBuilder.propertyValues(v);
}
mTimeHandlePropertyValue1 += System.currentTimeMillis() - start;
mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
}
}
@@ -770,15 +752,15 @@ public class VCardParser_V21 extends VCardParser {
* 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 data have it.
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) {
// TODO: limit should be set in accordance with propertyName?
StringBuilder builder = new StringBuilder();
ArrayList<String> list = new ArrayList<String>();
int length = propertyValue.length();
@@ -786,7 +768,7 @@ public class VCardParser_V21 extends VCardParser {
char ch = propertyValue.charAt(i);
if (ch == '\\' && i < length - 1) {
char nextCh = propertyValue.charAt(i + 1);
String unescapedString = maybeUnescape(nextCh);
String unescapedString = maybeUnescapeCharacter(nextCh);
if (unescapedString != null) {
builder.append(unescapedString);
i++;
@@ -819,7 +801,6 @@ public class VCardParser_V21 extends VCardParser {
throw new VCardNotSupportedException("AGENT Property is not supported now.");
/* This is insufficient support. Also, AGENT Property is very rare.
Ignore it for now.
TODO: fix this.
String[] strArray = propertyValue.split(":", 2);
if (!(strArray.length == 2 ||
@@ -843,7 +824,7 @@ public class VCardParser_V21 extends VCardParser {
* 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 maybeUnescape(char ch) {
protected String maybeUnescapeCharacter(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.
@@ -863,17 +844,11 @@ public class VCardParser_V21 extends VCardParser {
@Override
public boolean parse(InputStream is, String charset, VCardBuilder builder)
throws IOException, VCardException {
// TODO: make this count error entries instead of just throwing VCardException.
{
// TODO: If we really need to allow only CRLF as line break,
// we will have to develop our own BufferedReader().
final InputStreamReader tmpReader = new InputStreamReader(is, charset);
if (VCardConfig.showPerformanceLog()) {
mReader = new CustomBufferedReader(tmpReader);
} else {
mReader = new BufferedReader(tmpReader);
}
final InputStreamReader tmpReader = new InputStreamReader(is, charset);
if (VCardConfig.showPerformanceLog()) {
mReader = new CustomBufferedReader(tmpReader);
} else {
mReader = new BufferedReader(tmpReader);
}
mBuilder = builder;
@@ -903,21 +878,26 @@ public class VCardParser_V21 extends VCardParser {
}
private void showPerformanceInfo() {
Log.d(LOG_TAG, "total parsing time: " + mTimeTotal + " ms");
Log.d(LOG_TAG, "Total parsing time: " + mTimeTotal + " ms");
if (mReader instanceof CustomBufferedReader) {
Log.d(LOG_TAG, "total readLine time: " +
Log.d(LOG_TAG, "Total readLine time: " +
((CustomBufferedReader)mReader).getTotalmillisecond() + " ms");
}
Log.d(LOG_TAG, "mTimeStartRecord: " + mTimeStartRecord + " ms");
Log.d(LOG_TAG, "mTimeEndRecord: " + mTimeEndRecord + " ms");
Log.d(LOG_TAG, "mTimeParseItem1: " + mTimeParseItem1 + " ms");
Log.d(LOG_TAG, "mTimeParseItem2: " + mTimeParseItem2 + " ms");
Log.d(LOG_TAG, "mTimeParseItem3: " + mTimeParseItem3 + " ms");
Log.d(LOG_TAG, "mTimeHandlePropertyValue1: " + mTimeHandlePropertyValue1 + " ms");
Log.d(LOG_TAG, "mTimeHandlePropertyValue2: " + mTimeHandlePropertyValue2 + " ms");
Log.d(LOG_TAG, "mTimeHandlePropertyValue3: " + mTimeHandlePropertyValue3 + " 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;

View File

@@ -27,7 +27,7 @@ import java.util.HashSet;
* Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426)
*/
public class VCardParser_V30 extends VCardParser_V21 {
private static final String LOG_TAG = "VCardParser_V30";
private static final String LOG_TAG = "vcard.VCardParser_V30";
private static final HashSet<String> sAcceptablePropsWithParam = new HashSet<String>(
Arrays.asList(
@@ -49,7 +49,7 @@ public class VCardParser_V30 extends VCardParser_V21 {
@Override
protected String getVersion() {
return "3.0";
return Constants.VERSION_V30;
}
@Override
@@ -284,7 +284,7 @@ public class VCardParser_V30 extends VCardParser_V21 {
if (ch == '\\' && i < length - 1) {
char next_ch = text.charAt(++i);
if (next_ch == 'n' || next_ch == 'N') {
builder.append("\r\n");
builder.append("\n");
} else {
builder.append(next_ch);
}
@@ -296,9 +296,9 @@ public class VCardParser_V30 extends VCardParser_V21 {
}
@Override
protected String maybeUnescape(char ch) {
protected String maybeUnescapeCharacter(char ch) {
if (ch == 'n' || ch == 'N') {
return "\r\n";
return "\n";
} else {
return String.valueOf(ch);
}

View File

@@ -0,0 +1,764 @@
/*
* 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.content.ContentProviderOperation;
import android.content.ContentValues;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.text.TextUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Utilities for VCard handling codes.
*/
public class VCardUtils {
/*
* TODO: some of methods in this class should be placed to the more appropriate place...
*/
// Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
// converted to two attribute Strings. These only contain some minor fields valid in both
// vCard and current (as of 2009-08-07) Contacts structure.
private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
private static final Set<String> sPhoneTypesSetUnknownToContacts;
private static final Map<String, Integer> sKnownPhoneTypesMap_StoI;
static {
sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
sKnownPhoneTypesMap_StoI = new HashMap<String, Integer>();
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, Constants.ATTR_TYPE_CAR);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, Constants.ATTR_TYPE_PAGER);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER);
sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, Constants.ATTR_TYPE_ISDN);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_OTHER, Phone.TYPE_OTHER);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_CALLBACK, Phone.TYPE_CALLBACK);
sKnownPhoneTypesMap_StoI.put(
Constants.ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_RADIO, Phone.TYPE_RADIO);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TELEX, Phone.TYPE_TELEX);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TTY_TDD, Phone.TYPE_TTY_TDD);
sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_ASSISTANT, Phone.TYPE_ASSISTANT);
sPhoneTypesSetUnknownToContacts = new HashSet<String>();
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MODEM);
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MSG);
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_BBS);
sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_VIDEO);
}
public static String getPhoneAttributeString(int type) {
return sKnownPhoneTypesMap_ItoS.get(type);
}
/**
* Returns Interger when the given types can be parsed as known type. Returns String object
* when not, which should be set to label.
*/
public static Object getPhoneTypeFromStrings(Collection<String> types) {
int type = -1;
String label = null;
boolean isFax = false;
boolean hasPref = false;
if (types != null) {
for (String typeString : types) {
typeString = typeString.toUpperCase();
if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
hasPref = true;
} else if (typeString.equals(Constants.ATTR_TYPE_FAX)) {
isFax = true;
} else {
if (typeString.startsWith("X-") && type < 0) {
typeString = typeString.substring(2);
}
Integer tmp = sKnownPhoneTypesMap_StoI.get(typeString);
if (tmp != null) {
type = tmp;
} else if (type < 0) {
type = Phone.TYPE_CUSTOM;
label = typeString;
}
}
}
}
if (type < 0) {
if (hasPref) {
type = Phone.TYPE_MAIN;
} else {
// default to TYPE_HOME
type = Phone.TYPE_HOME;
}
}
if (isFax) {
if (type == Phone.TYPE_HOME) {
type = Phone.TYPE_FAX_HOME;
} else if (type == Phone.TYPE_WORK) {
type = Phone.TYPE_FAX_WORK;
} else if (type == Phone.TYPE_OTHER) {
type = Phone.TYPE_OTHER_FAX;
}
}
if (type == Phone.TYPE_CUSTOM) {
return label;
} else {
return type;
}
}
public static boolean isValidPhoneAttribute(String phoneAttribute, int vcardType) {
// TODO: check the following.
// - it may violate vCard spec
// - it may contain non-ASCII characters
//
// TODO: use vcardType
return (phoneAttribute.startsWith("X-") || phoneAttribute.startsWith("x-") ||
sPhoneTypesSetUnknownToContacts.contains(phoneAttribute));
}
public static String[] sortNameElements(int vcardType,
String familyName, String middleName, String givenName) {
String[] list = new String[3];
switch (VCardConfig.getNameOrderType(vcardType)) {
case VCardConfig.NAME_ORDER_JAPANESE:
// TODO: Should handle Ascii case?
list[0] = familyName;
list[1] = middleName;
list[2] = givenName;
break;
case VCardConfig.NAME_ORDER_EUROPE:
list[0] = middleName;
list[1] = givenName;
list[2] = familyName;
break;
default:
list[0] = givenName;
list[1] = middleName;
list[2] = familyName;
break;
}
return list;
}
/**
* Inserts postal data into the builder object.
*
* Note that the data structure of ContactsContract is different from that defined in vCard.
* So some conversion may be performed in this method. See also
* {{@link #getVCardPostalElements(ContentValues)}
*/
public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
final ContentProviderOperation.Builder builder,
final ContactStruct.PostalData postalData) {
builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
builder.withValue(StructuredPostal.TYPE, postalData.type);
if (postalData.type == StructuredPostal.TYPE_CUSTOM) {
builder.withValue(StructuredPostal.LABEL, postalData.label);
}
builder.withValue(StructuredPostal.POBOX, postalData.pobox);
// Extended address is dropped since there's no relevant entry in ContactsContract.
builder.withValue(StructuredPostal.STREET, postalData.street);
builder.withValue(StructuredPostal.CITY, postalData.localty);
builder.withValue(StructuredPostal.REGION, postalData.region);
builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode);
builder.withValue(StructuredPostal.COUNTRY, postalData.country);
builder.withValue(StructuredPostal.FORMATTED_ADDRESS,
postalData.getFormattedAddress(vcardType));
if (postalData.isPrimary) {
builder.withValue(Data.IS_PRIMARY, 1);
}
}
/**
* Returns String[] containing address information based on vCard spec
* (PO Box, Extended Address, Street, Locality, Region, Postal Code, Country Name).
* All String objects are non-null ("" is used when the relevant data is empty).
*
* Note that the data structure of ContactsContract is different from that defined in vCard.
* So some conversion may be performed in this method. See also
* {{@link #insertStructuredPostalDataUsingContactsStruct(int,
* android.content.ContentProviderOperation.Builder,
* android.pim.vcard.ContactStruct.PostalData)}
*/
public static String[] getVCardPostalElements(ContentValues contentValues) {
String[] dataArray = new String[7];
dataArray[0] = contentValues.getAsString(StructuredPostal.POBOX);
if (dataArray[0] == null) {
dataArray[0] = "";
}
// Extended addr. There's no relevant data in ContactsContract.
dataArray[1] = "";
dataArray[2] = contentValues.getAsString(StructuredPostal.STREET);
if (dataArray[2] == null) {
dataArray[2] = "";
}
// Assume that localty == city
dataArray[3] = contentValues.getAsString(StructuredPostal.CITY);
if (dataArray[3] == null) {
dataArray[3] = "";
}
String region = contentValues.getAsString(StructuredPostal.REGION);
if (!TextUtils.isEmpty(region)) {
dataArray[4] = region;
} else {
dataArray[4] = "";
}
dataArray[5] = contentValues.getAsString(StructuredPostal.POSTCODE);
if (dataArray[5] == null) {
dataArray[5] = "";
}
dataArray[6] = contentValues.getAsString(StructuredPostal.COUNTRY);
if (dataArray[6] == null) {
dataArray[6] = "";
}
return dataArray;
}
public static String constructNameFromElements(int nameOrderType,
String familyName, String middleName, String givenName) {
return constructNameFromElements(nameOrderType, familyName, middleName, givenName,
null, null);
}
public static String constructNameFromElements(int nameOrderType,
String familyName, String middleName, String givenName,
String prefix, String suffix) {
StringBuilder builder = new StringBuilder();
String[] nameList = sortNameElements(nameOrderType,
familyName, middleName, givenName);
boolean first = true;
if (!TextUtils.isEmpty(prefix)) {
first = false;
builder.append(prefix);
}
for (String namePart : nameList) {
if (!TextUtils.isEmpty(namePart)) {
if (first) {
first = false;
} else {
builder.append(' ');
}
builder.append(namePart);
}
}
if (!TextUtils.isEmpty(suffix)) {
if (!first) {
builder.append(' ');
}
builder.append(suffix);
}
return builder.toString();
}
public static boolean containsOnlyAscii(String str) {
if (TextUtils.isEmpty(str)) {
return true;
}
final int length = str.length();
final int asciiFirst = 0x20;
final int asciiLast = 0x126;
for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
int c = str.codePointAt(i);
if (c < asciiFirst || asciiLast < c) {
return false;
}
}
return true;
}
/**
* This is useful since vCard 3.0 often requires the ("X-") properties and groups
* should contain only alphabets, digits, and hyphen.
*
* Note: It is already known some devices (wrongly) outputs properties with characters
* which should not be in the field. One example is "X-GOOGLE TALK". We appreciate
* 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.
*/
public static boolean containsOnlyAlphaDigitHyphen(String str) {
if (TextUtils.isEmpty(str)) {
return true;
}
final int lowerAlphabetFirst = 0x41; // included ('A')
final int lowerAlphabetLast = 0x5b; // not included ('[')
final int upperAlphabetFirst = 0x61; // included ('a')
final int upperAlphabetLast = 0x7b; // included ('{')
final int digitFirst = 0x30; // included ('0')
final int digitLast = 0x39; // included ('9')
final int hyphen = '-';
final int length = str.length();
for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
int codepoint = str.codePointAt(i);
if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetLast) ||
(upperAlphabetFirst <= codepoint && codepoint < upperAlphabetLast) ||
(digitFirst <= codepoint && codepoint < digitLast) ||
(codepoint == hyphen))) {
return false;
}
}
return true;
}
// TODO: Replace wth the method in Base64 class.
private static char PAD = '=';
private static final char[] ENCODE64 = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
static public String encodeBase64(byte[] data) {
if (data == null) {
return "";
}
char[] charBuffer = new char[(data.length + 2) / 3 * 4];
int position = 0;
int _3byte = 0;
for (int i=0; i<data.length-2; i+=3) {
_3byte = ((data[i] & 0xFF) << 16) + ((data[i+1] & 0xFF) << 8) + (data[i+2] & 0xFF);
charBuffer[position++] = ENCODE64[_3byte >> 18];
charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
charBuffer[position++] = ENCODE64[_3byte & 0x3F];
}
switch(data.length % 3) {
case 1: // [111111][11 0000][0000 00][000000]
_3byte = ((data[data.length-1] & 0xFF) << 16);
charBuffer[position++] = ENCODE64[_3byte >> 18];
charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
charBuffer[position++] = PAD;
charBuffer[position++] = PAD;
break;
case 2: // [111111][11 1111][1111 00][000000]
_3byte = ((data[data.length-2] & 0xFF) << 16) + ((data[data.length-1] & 0xFF) << 8);
charBuffer[position++] = ENCODE64[_3byte >> 18];
charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
charBuffer[position++] = PAD;
break;
}
return new String(charBuffer);
}
static public String toHalfWidthString(String orgString) {
if (TextUtils.isEmpty(orgString)) {
return null;
}
StringBuilder builder = new StringBuilder();
int length = orgString.length();
for (int i = 0; i < length; i++) {
// All Japanese character is able to be expressed by char.
// Do not need to use String#codepPointAt().
char ch = orgString.charAt(i);
CharSequence halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
if (halfWidthText != null) {
builder.append(halfWidthText);
} else {
builder.append(ch);
}
}
return builder.toString();
}
private VCardUtils() {
}
}
/**
* TextUtils especially for Japanese.
* TODO: make this in android.text in the future
*/
class JapaneseUtils {
static private final Map<Character, String> sHalfWidthMap =
new HashMap<Character, String>();
static {
// There's no logical mapping rule in Unicode. Sigh.
sHalfWidthMap.put('\u3001', "\uFF64");
sHalfWidthMap.put('\u3002', "\uFF61");
sHalfWidthMap.put('\u300C', "\uFF62");
sHalfWidthMap.put('\u300D', "\uFF63");
sHalfWidthMap.put('\u301C', "~");
sHalfWidthMap.put('\u3041', "\uFF67");
sHalfWidthMap.put('\u3042', "\uFF71");
sHalfWidthMap.put('\u3043', "\uFF68");
sHalfWidthMap.put('\u3044', "\uFF72");
sHalfWidthMap.put('\u3045', "\uFF69");
sHalfWidthMap.put('\u3046', "\uFF73");
sHalfWidthMap.put('\u3047', "\uFF6A");
sHalfWidthMap.put('\u3048', "\uFF74");
sHalfWidthMap.put('\u3049', "\uFF6B");
sHalfWidthMap.put('\u304A', "\uFF75");
sHalfWidthMap.put('\u304B', "\uFF76");
sHalfWidthMap.put('\u304C', "\uFF76\uFF9E");
sHalfWidthMap.put('\u304D', "\uFF77");
sHalfWidthMap.put('\u304E', "\uFF77\uFF9E");
sHalfWidthMap.put('\u304F', "\uFF78");
sHalfWidthMap.put('\u3050', "\uFF78\uFF9E");
sHalfWidthMap.put('\u3051', "\uFF79");
sHalfWidthMap.put('\u3052', "\uFF79\uFF9E");
sHalfWidthMap.put('\u3053', "\uFF7A");
sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E");
sHalfWidthMap.put('\u3055', "\uFF7B");
sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E");
sHalfWidthMap.put('\u3057', "\uFF7C");
sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E");
sHalfWidthMap.put('\u3059', "\uFF7D");
sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E");
sHalfWidthMap.put('\u305B', "\uFF7E");
sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E");
sHalfWidthMap.put('\u305D', "\uFF7F");
sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E");
sHalfWidthMap.put('\u305F', "\uFF80");
sHalfWidthMap.put('\u3060', "\uFF80\uFF9E");
sHalfWidthMap.put('\u3061', "\uFF81");
sHalfWidthMap.put('\u3062', "\uFF81\uFF9E");
sHalfWidthMap.put('\u3063', "\uFF6F");
sHalfWidthMap.put('\u3064', "\uFF82");
sHalfWidthMap.put('\u3065', "\uFF82\uFF9E");
sHalfWidthMap.put('\u3066', "\uFF83");
sHalfWidthMap.put('\u3067', "\uFF83\uFF9E");
sHalfWidthMap.put('\u3068', "\uFF84");
sHalfWidthMap.put('\u3069', "\uFF84\uFF9E");
sHalfWidthMap.put('\u306A', "\uFF85");
sHalfWidthMap.put('\u306B', "\uFF86");
sHalfWidthMap.put('\u306C', "\uFF87");
sHalfWidthMap.put('\u306D', "\uFF88");
sHalfWidthMap.put('\u306E', "\uFF89");
sHalfWidthMap.put('\u306F', "\uFF8A");
sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E");
sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F");
sHalfWidthMap.put('\u3072', "\uFF8B");
sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E");
sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F");
sHalfWidthMap.put('\u3075', "\uFF8C");
sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E");
sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F");
sHalfWidthMap.put('\u3078', "\uFF8D");
sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E");
sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F");
sHalfWidthMap.put('\u307B', "\uFF8E");
sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E");
sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F");
sHalfWidthMap.put('\u307E', "\uFF8F");
sHalfWidthMap.put('\u307F', "\uFF90");
sHalfWidthMap.put('\u3080', "\uFF91");
sHalfWidthMap.put('\u3081', "\uFF92");
sHalfWidthMap.put('\u3082', "\uFF93");
sHalfWidthMap.put('\u3083', "\uFF6C");
sHalfWidthMap.put('\u3084', "\uFF94");
sHalfWidthMap.put('\u3085', "\uFF6D");
sHalfWidthMap.put('\u3086', "\uFF95");
sHalfWidthMap.put('\u3087', "\uFF6E");
sHalfWidthMap.put('\u3088', "\uFF96");
sHalfWidthMap.put('\u3089', "\uFF97");
sHalfWidthMap.put('\u308A', "\uFF98");
sHalfWidthMap.put('\u308B', "\uFF99");
sHalfWidthMap.put('\u308C', "\uFF9A");
sHalfWidthMap.put('\u308D', "\uFF9B");
sHalfWidthMap.put('\u308E', "\uFF9C");
sHalfWidthMap.put('\u308F', "\uFF9C");
sHalfWidthMap.put('\u3090', "\uFF72");
sHalfWidthMap.put('\u3091', "\uFF74");
sHalfWidthMap.put('\u3092', "\uFF66");
sHalfWidthMap.put('\u3093', "\uFF9D");
sHalfWidthMap.put('\u309B', "\uFF9E");
sHalfWidthMap.put('\u309C', "\uFF9F");
sHalfWidthMap.put('\u30A1', "\uFF67");
sHalfWidthMap.put('\u30A2', "\uFF71");
sHalfWidthMap.put('\u30A3', "\uFF68");
sHalfWidthMap.put('\u30A4', "\uFF72");
sHalfWidthMap.put('\u30A5', "\uFF69");
sHalfWidthMap.put('\u30A6', "\uFF73");
sHalfWidthMap.put('\u30A7', "\uFF6A");
sHalfWidthMap.put('\u30A8', "\uFF74");
sHalfWidthMap.put('\u30A9', "\uFF6B");
sHalfWidthMap.put('\u30AA', "\uFF75");
sHalfWidthMap.put('\u30AB', "\uFF76");
sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E");
sHalfWidthMap.put('\u30AD', "\uFF77");
sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E");
sHalfWidthMap.put('\u30AF', "\uFF78");
sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E");
sHalfWidthMap.put('\u30B1', "\uFF79");
sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E");
sHalfWidthMap.put('\u30B3', "\uFF7A");
sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E");
sHalfWidthMap.put('\u30B5', "\uFF7B");
sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E");
sHalfWidthMap.put('\u30B7', "\uFF7C");
sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E");
sHalfWidthMap.put('\u30B9', "\uFF7D");
sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E");
sHalfWidthMap.put('\u30BB', "\uFF7E");
sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E");
sHalfWidthMap.put('\u30BD', "\uFF7F");
sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E");
sHalfWidthMap.put('\u30BF', "\uFF80");
sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E");
sHalfWidthMap.put('\u30C1', "\uFF81");
sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E");
sHalfWidthMap.put('\u30C3', "\uFF6F");
sHalfWidthMap.put('\u30C4', "\uFF82");
sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E");
sHalfWidthMap.put('\u30C6', "\uFF83");
sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E");
sHalfWidthMap.put('\u30C8', "\uFF84");
sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E");
sHalfWidthMap.put('\u30CA', "\uFF85");
sHalfWidthMap.put('\u30CB', "\uFF86");
sHalfWidthMap.put('\u30CC', "\uFF87");
sHalfWidthMap.put('\u30CD', "\uFF88");
sHalfWidthMap.put('\u30CE', "\uFF89");
sHalfWidthMap.put('\u30CF', "\uFF8A");
sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E");
sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F");
sHalfWidthMap.put('\u30D2', "\uFF8B");
sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E");
sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F");
sHalfWidthMap.put('\u30D5', "\uFF8C");
sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E");
sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F");
sHalfWidthMap.put('\u30D8', "\uFF8D");
sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E");
sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F");
sHalfWidthMap.put('\u30DB', "\uFF8E");
sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E");
sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F");
sHalfWidthMap.put('\u30DE', "\uFF8F");
sHalfWidthMap.put('\u30DF', "\uFF90");
sHalfWidthMap.put('\u30E0', "\uFF91");
sHalfWidthMap.put('\u30E1', "\uFF92");
sHalfWidthMap.put('\u30E2', "\uFF93");
sHalfWidthMap.put('\u30E3', "\uFF6C");
sHalfWidthMap.put('\u30E4', "\uFF94");
sHalfWidthMap.put('\u30E5', "\uFF6D");
sHalfWidthMap.put('\u30E6', "\uFF95");
sHalfWidthMap.put('\u30E7', "\uFF6E");
sHalfWidthMap.put('\u30E8', "\uFF96");
sHalfWidthMap.put('\u30E9', "\uFF97");
sHalfWidthMap.put('\u30EA', "\uFF98");
sHalfWidthMap.put('\u30EB', "\uFF99");
sHalfWidthMap.put('\u30EC', "\uFF9A");
sHalfWidthMap.put('\u30ED', "\uFF9B");
sHalfWidthMap.put('\u30EE', "\uFF9C");
sHalfWidthMap.put('\u30EF', "\uFF9C");
sHalfWidthMap.put('\u30F0', "\uFF72");
sHalfWidthMap.put('\u30F1', "\uFF74");
sHalfWidthMap.put('\u30F2', "\uFF66");
sHalfWidthMap.put('\u30F3', "\uFF9D");
sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E");
sHalfWidthMap.put('\u30F5', "\uFF76");
sHalfWidthMap.put('\u30F6', "\uFF79");
sHalfWidthMap.put('\u30FB', "\uFF65");
sHalfWidthMap.put('\u30FC', "\uFF70");
sHalfWidthMap.put('\uFF01', "!");
sHalfWidthMap.put('\uFF02', "\"");
sHalfWidthMap.put('\uFF03', "#");
sHalfWidthMap.put('\uFF04', "$");
sHalfWidthMap.put('\uFF05', "%");
sHalfWidthMap.put('\uFF06', "&");
sHalfWidthMap.put('\uFF07', "'");
sHalfWidthMap.put('\uFF08', "(");
sHalfWidthMap.put('\uFF09', ")");
sHalfWidthMap.put('\uFF0A', "*");
sHalfWidthMap.put('\uFF0B', "+");
sHalfWidthMap.put('\uFF0C', ",");
sHalfWidthMap.put('\uFF0D', "-");
sHalfWidthMap.put('\uFF0E', ".");
sHalfWidthMap.put('\uFF0F', "/");
sHalfWidthMap.put('\uFF10', "0");
sHalfWidthMap.put('\uFF11', "1");
sHalfWidthMap.put('\uFF12', "2");
sHalfWidthMap.put('\uFF13', "3");
sHalfWidthMap.put('\uFF14', "4");
sHalfWidthMap.put('\uFF15', "5");
sHalfWidthMap.put('\uFF16', "6");
sHalfWidthMap.put('\uFF17', "7");
sHalfWidthMap.put('\uFF18', "8");
sHalfWidthMap.put('\uFF19', "9");
sHalfWidthMap.put('\uFF1A', ":");
sHalfWidthMap.put('\uFF1B', ";");
sHalfWidthMap.put('\uFF1C', "<");
sHalfWidthMap.put('\uFF1D', "=");
sHalfWidthMap.put('\uFF1E', ">");
sHalfWidthMap.put('\uFF1F', "?");
sHalfWidthMap.put('\uFF20', "@");
sHalfWidthMap.put('\uFF21', "A");
sHalfWidthMap.put('\uFF22', "B");
sHalfWidthMap.put('\uFF23', "C");
sHalfWidthMap.put('\uFF24', "D");
sHalfWidthMap.put('\uFF25', "E");
sHalfWidthMap.put('\uFF26', "F");
sHalfWidthMap.put('\uFF27', "G");
sHalfWidthMap.put('\uFF28', "H");
sHalfWidthMap.put('\uFF29', "I");
sHalfWidthMap.put('\uFF2A', "J");
sHalfWidthMap.put('\uFF2B', "K");
sHalfWidthMap.put('\uFF2C', "L");
sHalfWidthMap.put('\uFF2D', "M");
sHalfWidthMap.put('\uFF2E', "N");
sHalfWidthMap.put('\uFF2F', "O");
sHalfWidthMap.put('\uFF30', "P");
sHalfWidthMap.put('\uFF31', "Q");
sHalfWidthMap.put('\uFF32', "R");
sHalfWidthMap.put('\uFF33', "S");
sHalfWidthMap.put('\uFF34', "T");
sHalfWidthMap.put('\uFF35', "U");
sHalfWidthMap.put('\uFF36', "V");
sHalfWidthMap.put('\uFF37', "W");
sHalfWidthMap.put('\uFF38', "X");
sHalfWidthMap.put('\uFF39', "Y");
sHalfWidthMap.put('\uFF3A', "Z");
sHalfWidthMap.put('\uFF3B', "[");
sHalfWidthMap.put('\uFF3C', "\\");
sHalfWidthMap.put('\uFF3D', "]");
sHalfWidthMap.put('\uFF3E', "^");
sHalfWidthMap.put('\uFF3F', "_");
sHalfWidthMap.put('\uFF41', "a");
sHalfWidthMap.put('\uFF42', "b");
sHalfWidthMap.put('\uFF43', "c");
sHalfWidthMap.put('\uFF44', "d");
sHalfWidthMap.put('\uFF45', "e");
sHalfWidthMap.put('\uFF46', "f");
sHalfWidthMap.put('\uFF47', "g");
sHalfWidthMap.put('\uFF48', "h");
sHalfWidthMap.put('\uFF49', "i");
sHalfWidthMap.put('\uFF4A', "j");
sHalfWidthMap.put('\uFF4B', "k");
sHalfWidthMap.put('\uFF4C', "l");
sHalfWidthMap.put('\uFF4D', "m");
sHalfWidthMap.put('\uFF4E', "n");
sHalfWidthMap.put('\uFF4F', "o");
sHalfWidthMap.put('\uFF50', "p");
sHalfWidthMap.put('\uFF51', "q");
sHalfWidthMap.put('\uFF52', "r");
sHalfWidthMap.put('\uFF53', "s");
sHalfWidthMap.put('\uFF54', "t");
sHalfWidthMap.put('\uFF55', "u");
sHalfWidthMap.put('\uFF56', "v");
sHalfWidthMap.put('\uFF57', "w");
sHalfWidthMap.put('\uFF58', "x");
sHalfWidthMap.put('\uFF59', "y");
sHalfWidthMap.put('\uFF5A', "z");
sHalfWidthMap.put('\uFF5B', "{");
sHalfWidthMap.put('\uFF5C', "|");
sHalfWidthMap.put('\uFF5D', "}");
sHalfWidthMap.put('\uFF5E', "~");
sHalfWidthMap.put('\uFF61', "\uFF61");
sHalfWidthMap.put('\uFF62', "\uFF62");
sHalfWidthMap.put('\uFF63', "\uFF63");
sHalfWidthMap.put('\uFF64', "\uFF64");
sHalfWidthMap.put('\uFF65', "\uFF65");
sHalfWidthMap.put('\uFF66', "\uFF66");
sHalfWidthMap.put('\uFF67', "\uFF67");
sHalfWidthMap.put('\uFF68', "\uFF68");
sHalfWidthMap.put('\uFF69', "\uFF69");
sHalfWidthMap.put('\uFF6A', "\uFF6A");
sHalfWidthMap.put('\uFF6B', "\uFF6B");
sHalfWidthMap.put('\uFF6C', "\uFF6C");
sHalfWidthMap.put('\uFF6D', "\uFF6D");
sHalfWidthMap.put('\uFF6E', "\uFF6E");
sHalfWidthMap.put('\uFF6F', "\uFF6F");
sHalfWidthMap.put('\uFF70', "\uFF70");
sHalfWidthMap.put('\uFF71', "\uFF71");
sHalfWidthMap.put('\uFF72', "\uFF72");
sHalfWidthMap.put('\uFF73', "\uFF73");
sHalfWidthMap.put('\uFF74', "\uFF74");
sHalfWidthMap.put('\uFF75', "\uFF75");
sHalfWidthMap.put('\uFF76', "\uFF76");
sHalfWidthMap.put('\uFF77', "\uFF77");
sHalfWidthMap.put('\uFF78', "\uFF78");
sHalfWidthMap.put('\uFF79', "\uFF79");
sHalfWidthMap.put('\uFF7A', "\uFF7A");
sHalfWidthMap.put('\uFF7B', "\uFF7B");
sHalfWidthMap.put('\uFF7C', "\uFF7C");
sHalfWidthMap.put('\uFF7D', "\uFF7D");
sHalfWidthMap.put('\uFF7E', "\uFF7E");
sHalfWidthMap.put('\uFF7F', "\uFF7F");
sHalfWidthMap.put('\uFF80', "\uFF80");
sHalfWidthMap.put('\uFF81', "\uFF81");
sHalfWidthMap.put('\uFF82', "\uFF82");
sHalfWidthMap.put('\uFF83', "\uFF83");
sHalfWidthMap.put('\uFF84', "\uFF84");
sHalfWidthMap.put('\uFF85', "\uFF85");
sHalfWidthMap.put('\uFF86', "\uFF86");
sHalfWidthMap.put('\uFF87', "\uFF87");
sHalfWidthMap.put('\uFF88', "\uFF88");
sHalfWidthMap.put('\uFF89', "\uFF89");
sHalfWidthMap.put('\uFF8A', "\uFF8A");
sHalfWidthMap.put('\uFF8B', "\uFF8B");
sHalfWidthMap.put('\uFF8C', "\uFF8C");
sHalfWidthMap.put('\uFF8D', "\uFF8D");
sHalfWidthMap.put('\uFF8E', "\uFF8E");
sHalfWidthMap.put('\uFF8F', "\uFF8F");
sHalfWidthMap.put('\uFF90', "\uFF90");
sHalfWidthMap.put('\uFF91', "\uFF91");
sHalfWidthMap.put('\uFF92', "\uFF92");
sHalfWidthMap.put('\uFF93', "\uFF93");
sHalfWidthMap.put('\uFF94', "\uFF94");
sHalfWidthMap.put('\uFF95', "\uFF95");
sHalfWidthMap.put('\uFF96', "\uFF96");
sHalfWidthMap.put('\uFF97', "\uFF97");
sHalfWidthMap.put('\uFF98', "\uFF98");
sHalfWidthMap.put('\uFF99', "\uFF99");
sHalfWidthMap.put('\uFF9A', "\uFF9A");
sHalfWidthMap.put('\uFF9B', "\uFF9B");
sHalfWidthMap.put('\uFF9C', "\uFF9C");
sHalfWidthMap.put('\uFF9D', "\uFF9D");
sHalfWidthMap.put('\uFF9E', "\uFF9E");
sHalfWidthMap.put('\uFF9F', "\uFF9F");
sHalfWidthMap.put('\uFFE5', "\u005C\u005C");
}
/**
* Return half-width version of that character if possible. Return null if not possible
* @param ch input character
* @return CharSequence object if the mapping for ch exists. Return null otherwise.
*/
public static CharSequence tryGetHalfWidthText(char ch) {
if (sHalfWidthMap.containsKey(ch)) {
return sHalfWidthMap.get(ch);
} else {
return null;
}
}
}

View File

@@ -28,6 +28,7 @@ import android.syncml.pim.PropertyNode;
import android.syncml.pim.VBuilder;
import android.syncml.pim.VNode;
import android.syncml.pim.VParser;
import android.text.TextUtils;
import android.util.CharsetUtils;
import android.util.Log;
@@ -403,7 +404,10 @@ public class VCardDataBuilder implements VBuilder {
String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
String encoding = paramMap.getAsString("ENCODING");
if (targetCharset == null || targetCharset.length() == 0) {
Log.d("@@@", String.format("targetCharset: \"%s\", encoding: \"%s\"",
targetCharset, encoding));
if (TextUtils.isEmpty(targetCharset)) {
targetCharset = mTargetCharset;
}

View File

@@ -17,7 +17,6 @@
package android.syncml.pim.vcard;
import android.syncml.pim.VDataBuilder;
import android.syncml.pim.VParser;
import android.util.Config;
import android.util.Log;

View File

@@ -35,10 +35,6 @@
<!-- The duration (in milliseconds) of a long animation. -->
<integer name="config_longAnimTime">300</integer>
<!-- Flag indicating whether Last Name comes before First Name.
This becomes true in Japan, for example.-->
<bool name="config_lastname_comes_before_firstname">false</bool>
<!-- This string array should be overridden by the device to present a list of network attributes. This is used by the connectivity manager to decide which networks can coexist based on the hardward -->
<!-- An Array of "[type-name],[associated radio-name],[priority] -->
<string-array translatable="false" name="networkAttributes">

View File

@@ -19,11 +19,6 @@ package com.android.unit_tests.vcard;
import android.content.ContentValues;
import android.pim.vcard.ContactStruct;
import android.pim.vcard.EntryHandler;
import android.pim.vcard.VCardBuilder;
import android.pim.vcard.VCardBuilderCollection;
import android.pim.vcard.VCardConfig;
import android.pim.vcard.VCardDataBuilder;
import android.pim.vcard.VCardParser;
import android.pim.vcard.VCardParser_V21;
import android.pim.vcard.VCardParser_V30;
import android.pim.vcard.exception.VCardException;
@@ -38,20 +33,21 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public class VCardTests extends AndroidTestCase {
// TODO: Use EntityIterator, which is added in Eclair.
private static class EntryHolder implements EntryHandler {
public List<ContactStruct> contacts = new ArrayList<ContactStruct>();
public void onParsingStart() {
}
public void onEntryCreated(ContactStruct contactStruct) {
contacts.add(contactStruct);
}
public void onFinal() {
public void onParsingEnd() {
}
}
/*
static void verify(ContactStruct expected, ContactStruct actual) {
if (!equalsString(expected.getName(), actual.getName())) {
fail(String.format("Names do not equal: \"%s\" != \"%s\"",
@@ -128,7 +124,7 @@ public class VCardTests extends AndroidTestCase {
}
}
}
}
}*/
private class PropertyNodesVerifier {
private HashMap<String, ArrayList<PropertyNode>> mPropertyNodeMap;
@@ -189,6 +185,7 @@ public class VCardTests extends AndroidTestCase {
}
}
/*
public void testV21SimpleCase1_1() throws IOException, VCardException {
VCardParser parser = new VCardParser_V21();
VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH);
@@ -260,7 +257,7 @@ public class VCardTests extends AndroidTestCase {
verify(new ContactStruct("Ando Roid", null,
null, null, null, null, null, null),
actual);
}
}*/
public void testV21BackslashCase() throws IOException, VCardException {
VCardParser_V21 parser = new VCardParser_V21();