Modify vCard importer/exporter around sip handling
Bug: 2985401
Change-Id: I8216dd80bcb09669be807dd92452e69fe2a11a65
diff --git a/java/com/android/vcard/VCardBuilder.java b/java/com/android/vcard/VCardBuilder.java
index f063a07..2eec998 100644
--- a/java/com/android/vcard/VCardBuilder.java
+++ b/java/com/android/vcard/VCardBuilder.java
@@ -1773,8 +1773,6 @@
* instead of "IMPP;sip:...".
*
* We honor RFC 4770 and don't allow vCard 3.0 to emit X-SIP at all.
- *
- * vCard 4.0 is aware of RFC 4770, so just using IMPP would be fine.
*/
public VCardBuilder appendSipAddresses(final List<ContentValues> contentValuesList) {
final boolean useXProperty;
@@ -1803,11 +1801,19 @@
// No type is available yet.
appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_X_SIP, sipAddress);
} else {
- // IMPP is not just for SIP but the other protcols like XMPP.
if (!sipAddress.startsWith("sip:")) {
sipAddress = "sip:" + sipAddress;
}
- appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_IMPP, sipAddress);
+ final String propertyName;
+ if (VCardConfig.isVersion40(mVCardType)) {
+ // We have two ways to emit sip address: TEL and IMPP. Currently (rev.13)
+ // TEL seems appropriate but may change in the future.
+ propertyName = VCardConstants.PROPERTY_TEL;
+ } else {
+ // RFC 4770 (for vCard 3.0)
+ propertyName = VCardConstants.PROPERTY_IMPP;
+ }
+ appendLineWithCharsetAndQPDetection(propertyName, sipAddress);
}
}
}
diff --git a/java/com/android/vcard/VCardEntry.java b/java/com/android/vcard/VCardEntry.java
index b5ed10f..66104bf 100644
--- a/java/com/android/vcard/VCardEntry.java
+++ b/java/com/android/vcard/VCardEntry.java
@@ -49,8 +49,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* This class bridges between data structure of Contact app and VCard data.
@@ -470,7 +472,7 @@
private List<ImData> mImList;
private List<PhotoData> mPhotoList;
private List<String> mWebsiteList;
- private List<String> mSipList;
+ private Set<String> mSipSet;
private List<List<String>> mAndroidCustomPropertyList;
private final int mVCardType;
@@ -977,9 +979,36 @@
addPhotoBytes(formatName, propBytes, isPrimary);
}
} else if (propName.equals(VCardConstants.PROPERTY_TEL)) {
+ final String phoneNumber;
+ if (VCardConfig.isVersion40(mVCardType)) {
+ // Given propValue is in URI format, not in phone number format used until
+ // vCard 3.0.
+ if (propValue.startsWith("sip:") ) {
+ if (propValue.length() > 4) {
+ if (mSipSet == null) {
+ mSipSet = new LinkedHashSet<String>();
+ }
+ mSipSet.add(propValue.substring(4));
+ }
+ return;
+ } else if (propValue.startsWith("tel:")) {
+ phoneNumber = propValue.substring(4);
+ } else {
+ // We don't know appropriate way to handle the other schemas. Also,
+ // we may still have non-URI phone number. To keep given data as much as
+ // we can, just save original value here.
+ phoneNumber = propValue;
+ }
+ } else {
+ phoneNumber = propValue;
+ }
+
+ if (propValue.length() == 0) {
+ return;
+ }
final Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
final Object typeObject =
- VCardUtils.getPhoneTypeFromStrings(typeCollection, propValue);
+ VCardUtils.getPhoneTypeFromStrings(typeCollection, phoneNumber);
final int type;
final String label;
if (typeObject instanceof Integer) {
@@ -991,12 +1020,13 @@
}
final boolean isPrimary;
- if (typeCollection != null && typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) {
+ if (typeCollection != null &&
+ typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) {
isPrimary = true;
} else {
isPrimary = false;
}
- addPhone(type, propValue, label, isPrimary);
+ addPhone(type, phoneNumber, label, isPrimary);
} else if (propName.equals(VCardConstants.PROPERTY_X_SKYPE_PSTNNUMBER)) {
// The phone number available via Skype.
Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
@@ -1050,24 +1080,26 @@
} else if (propName.equals(VCardConstants.PROPERTY_IMPP)) {
// See also RFC 4770 (for vCard 3.0)
if (propValue.startsWith("sip:") && propValue.length() > 4) {
- if (mSipList == null) {
- mSipList = new ArrayList<String>();
+ if (mSipSet == null) {
+ mSipSet = new LinkedHashSet<String>();
}
- mSipList.add(propValue.substring(4));
+ mSipSet.add(propValue.substring(4));
}
} else if (propName.equals(VCardConstants.PROPERTY_X_SIP)) {
if (!TextUtils.isEmpty(propValue)) {
- if (mSipList == null) {
- mSipList = new ArrayList<String>();
+ if (mSipSet == null) {
+ mSipSet = new LinkedHashSet<String>();
}
- final String sipAddress;
- if (propValue.startsWith("sip:") && propValue.length() > 4) {
- sipAddress = propValue.substring(4);
+ if (propValue.startsWith("sip:")) {
+ if (propValue.length() > 4) {
+ mSipSet.add(propValue.substring(4));
+ } else {
+ // Empty sip value. Ignore.
+ }
} else {
- sipAddress = propValue;
+ mSipSet.add(propValue);
}
- mSipList.add(sipAddress);
}
} else if (propName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) {
final List<String> customPropertyList =
@@ -1075,6 +1107,7 @@
handleAndroidCustomProperty(customPropertyList);
} else {
}
+ // Be careful when adding some logic here, as some blocks above may use "return".
}
private void handleAndroidCustomProperty(final List<String> customPropertyList) {
@@ -1320,8 +1353,8 @@
operationList.add(builder.build());
}
- if (mSipList != null && !mSipList.isEmpty()) {
- for (String sipAddress : mSipList) {
+ if (mSipSet != null && !mSipSet.isEmpty()) {
+ for (String sipAddress : mSipSet) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Event.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
diff --git a/tests/res/raw/v40_sip.vcf b/tests/res/raw/v40_sip.vcf
new file mode 100644
index 0000000..7b45ce3
--- /dev/null
+++ b/tests/res/raw/v40_sip.vcf
@@ -0,0 +1,7 @@
+BEGIN:VCARD
+VERSION:4.0
+FN:安藤 ロイド
+N:安藤;ロイド;;;
+TEL;TYPE=HOME:tel:1
+TEL:sip:example@example.com
+END:VCARD
diff --git a/tests/src/com/android/vcard/tests/VCardExporterTests.java b/tests/src/com/android/vcard/tests/VCardExporterTests.java
index 8fb0685..76bd755 100644
--- a/tests/src/com/android/vcard/tests/VCardExporterTests.java
+++ b/tests/src/com/android/vcard/tests/VCardExporterTests.java
@@ -1299,8 +1299,8 @@
.put(SipAddress.SIP_ADDRESS, "android@example.com");
mVerifier.addLineVerifierElem()
.addExpected("FN:")
- .addExpected("IMPP:sip:android@example.com");
+ .addExpected("TEL:sip:android@example.com");
mVerifier.addPropertyNodesVerifierElemWithEmptyName()
- .addExpectedNode("IMPP", "sip:android@example.com");
+ .addExpectedNode("TEL", "sip:android@example.com");
}
}
diff --git a/tests/src/com/android/vcard/tests/VCardImporterTests.java b/tests/src/com/android/vcard/tests/VCardImporterTests.java
index 36cc085..2900106 100644
--- a/tests/src/com/android/vcard/tests/VCardImporterTests.java
+++ b/tests/src/com/android/vcard/tests/VCardImporterTests.java
@@ -1144,4 +1144,18 @@
elem.addExpected(SipAddress.CONTENT_ITEM_TYPE)
.put(SipAddress.SIP_ADDRESS, "90-180-360");
}
+
+ public void testSipV40() {
+ mVerifier.initForImportTest(V40, R.raw.v40_sip);
+ final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4")
+ .put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9")
+ .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_HOME)
+ .put(Phone.NUMBER, "1");
+ elem.addExpected(SipAddress.CONTENT_ITEM_TYPE)
+ .put(SipAddress.SIP_ADDRESS, "example@example.com");
+ }
}