Loading src/main/java/at/bitfire/vcard4android/ContactWriter.kt +11 −5 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ package at.bitfire.vcard4android import at.bitfire.vcard4android.Utils.isEmpty import at.bitfire.vcard4android.property.* import at.bitfire.vcard4android.property.CustomScribes.registerCustomScribes import ezvcard.Ezvcard Loading Loading @@ -51,7 +52,6 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard Contact.productID?.let { vCard.setProductId(it) } addKindAndMembers() addFormattedName() addStructuredName() addPhoneticName() Loading Loading @@ -227,11 +227,16 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard if (version == VCardVersion.V4_0) { // add N only if there's some data in it if (n.prefixes.isNotEmpty() || n.given != null || n.additionalNames.isNotEmpty() || n.family != null || n.suffixes.isNotEmpty()) if (!n.isEmpty()) vCard.structuredName = n } else /* version == VCardVersion.V3_0 */ { } else if (version == VCardVersion.V3_0) { // vCard 3 REQUIRES N [RFC 2426 p. 29] // don't use empty N for vCard3 groups (otherwise they appear with name ";;;;;" in Apple) if (contact.group && n.isEmpty()) n.family = contact.displayName vCard.structuredName = n } } Loading Loading @@ -337,8 +342,9 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard isAddProdId = Contact.productID == null registerCustomScribes() // include trailing semicolons for maximum compatibility isIncludeTrailingSemicolons = true /* include trailing semicolons for maximum compatibility Don't include trailing semicolons for groups because Apple then shows "N:Group;;;;" as "Group;;;;". */ isIncludeTrailingSemicolons = !contact.group // use caret encoding for parameter values (RFC 6868) isCaretEncodingEnabled = true Loading src/main/java/at/bitfire/vcard4android/Utils.kt +4 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ import android.database.Cursor import android.database.DatabaseUtils import android.net.Uri import android.provider.ContactsContract import ezvcard.property.StructuredName object Utils { Loading @@ -19,6 +20,9 @@ object Utils { return values } fun StructuredName.isEmpty() = prefixes.isEmpty() && given == null && additionalNames.isEmpty() && family == null && suffixes.isEmpty() fun Uri.asSyncAdapter(account: Account): Uri = buildUpon() .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type) Loading src/test/java/at/bitfire/vcard4android/ContactWriterTest.kt +19 −11 Original line number Diff line number Diff line Loading @@ -186,32 +186,40 @@ class ContactWriterTest { @Test fun testImpp() { val vCard = generate { impps.add(LabeledProperty(Impp.xmpp("test@example.com"))) } assertEquals(URI("xmpp:test@example.com"), vCard.impps.first().uri) } @Test fun testKindAndMember_vCard3() { fun testGroup_vCard3() { val vCard = generate(VCardVersion.V3_0) { group = true members += "member1" displayName = "Sample vCard3 Group" } assertEquals(Kind.GROUP, vCard.getProperty(XAddressBookServerKind::class.java).value) assertEquals("urn:uuid:member1", vCard.getProperty(XAddressBookServerMember::class.java).value) assertEquals("Sample vCard3 Group", vCard.formattedName.value) assertEquals(StructuredName().apply { family = "Sample vCard3 Group" }, vCard.structuredName) } @Test fun testKindAndMember_vCard4() { fun testGroup_vCard4() { val vCard = generate(VCardVersion.V4_0) { group = true members += "member1" displayName = "Sample vCard4 Group" } assertEquals(Kind.GROUP, vCard.getProperty(Kind::class.java).value) assertEquals("urn:uuid:member1", vCard.members.first().value) assertEquals("Sample vCard4 Group", vCard.formattedName.value) assertNull(vCard.structuredName) } @Test fun testImpp() { val vCard = generate { impps.add(LabeledProperty(Impp.xmpp("test@example.com"))) } assertEquals(URI("xmpp:test@example.com"), vCard.impps.first().uri) } Loading Loading
src/main/java/at/bitfire/vcard4android/ContactWriter.kt +11 −5 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ package at.bitfire.vcard4android import at.bitfire.vcard4android.Utils.isEmpty import at.bitfire.vcard4android.property.* import at.bitfire.vcard4android.property.CustomScribes.registerCustomScribes import ezvcard.Ezvcard Loading Loading @@ -51,7 +52,6 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard Contact.productID?.let { vCard.setProductId(it) } addKindAndMembers() addFormattedName() addStructuredName() addPhoneticName() Loading Loading @@ -227,11 +227,16 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard if (version == VCardVersion.V4_0) { // add N only if there's some data in it if (n.prefixes.isNotEmpty() || n.given != null || n.additionalNames.isNotEmpty() || n.family != null || n.suffixes.isNotEmpty()) if (!n.isEmpty()) vCard.structuredName = n } else /* version == VCardVersion.V3_0 */ { } else if (version == VCardVersion.V3_0) { // vCard 3 REQUIRES N [RFC 2426 p. 29] // don't use empty N for vCard3 groups (otherwise they appear with name ";;;;;" in Apple) if (contact.group && n.isEmpty()) n.family = contact.displayName vCard.structuredName = n } } Loading Loading @@ -337,8 +342,9 @@ class ContactWriter private constructor(val contact: Contact, val version: VCard isAddProdId = Contact.productID == null registerCustomScribes() // include trailing semicolons for maximum compatibility isIncludeTrailingSemicolons = true /* include trailing semicolons for maximum compatibility Don't include trailing semicolons for groups because Apple then shows "N:Group;;;;" as "Group;;;;". */ isIncludeTrailingSemicolons = !contact.group // use caret encoding for parameter values (RFC 6868) isCaretEncodingEnabled = true Loading
src/main/java/at/bitfire/vcard4android/Utils.kt +4 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ import android.database.Cursor import android.database.DatabaseUtils import android.net.Uri import android.provider.ContactsContract import ezvcard.property.StructuredName object Utils { Loading @@ -19,6 +20,9 @@ object Utils { return values } fun StructuredName.isEmpty() = prefixes.isEmpty() && given == null && additionalNames.isEmpty() && family == null && suffixes.isEmpty() fun Uri.asSyncAdapter(account: Account): Uri = buildUpon() .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type) Loading
src/test/java/at/bitfire/vcard4android/ContactWriterTest.kt +19 −11 Original line number Diff line number Diff line Loading @@ -186,32 +186,40 @@ class ContactWriterTest { @Test fun testImpp() { val vCard = generate { impps.add(LabeledProperty(Impp.xmpp("test@example.com"))) } assertEquals(URI("xmpp:test@example.com"), vCard.impps.first().uri) } @Test fun testKindAndMember_vCard3() { fun testGroup_vCard3() { val vCard = generate(VCardVersion.V3_0) { group = true members += "member1" displayName = "Sample vCard3 Group" } assertEquals(Kind.GROUP, vCard.getProperty(XAddressBookServerKind::class.java).value) assertEquals("urn:uuid:member1", vCard.getProperty(XAddressBookServerMember::class.java).value) assertEquals("Sample vCard3 Group", vCard.formattedName.value) assertEquals(StructuredName().apply { family = "Sample vCard3 Group" }, vCard.structuredName) } @Test fun testKindAndMember_vCard4() { fun testGroup_vCard4() { val vCard = generate(VCardVersion.V4_0) { group = true members += "member1" displayName = "Sample vCard4 Group" } assertEquals(Kind.GROUP, vCard.getProperty(Kind::class.java).value) assertEquals("urn:uuid:member1", vCard.members.first().value) assertEquals("Sample vCard4 Group", vCard.formattedName.value) assertNull(vCard.structuredName) } @Test fun testImpp() { val vCard = generate { impps.add(LabeledProperty(Impp.xmpp("test@example.com"))) } assertEquals(URI("xmpp:test@example.com"), vCard.impps.first().uri) } Loading