From 603758fcbc3eaed4ab41af77c2b33f4ac7650908 Mon Sep 17 00:00:00 2001 From: rfc2822 Date: Sat, 2 Nov 2013 16:09:48 +0100 Subject: [PATCH] support for structured addresses (closes #29); bug fixes --- res/xml/contacts.xml | 7 ++ src/at/bitfire/davdroid/resource/Contact.java | 30 ++++---- .../davdroid/resource/LocalAddressBook.java | 68 +++++++++++++++++++ .../davdroid/resource/RemoteCollection.java | 9 ++- .../davdroid/webdav/WebDavResource.java | 10 +-- 5 files changed, 106 insertions(+), 18 deletions(-) diff --git a/res/xml/contacts.xml b/res/xml/contacts.xml index 7ea6e561..57aea051 100644 --- a/res/xml/contacts.xml +++ b/res/xml/contacts.xml @@ -37,6 +37,13 @@ + + + + + + + diff --git a/src/at/bitfire/davdroid/resource/Contact.java b/src/at/bitfire/davdroid/resource/Contact.java index dc45b71e..c55b686a 100644 --- a/src/at/bitfire/davdroid/resource/Contact.java +++ b/src/at/bitfire/davdroid/resource/Contact.java @@ -23,7 +23,6 @@ import lombok.ToString; import net.fortuna.ical4j.data.ParserException; import net.fortuna.ical4j.model.Date; import net.fortuna.ical4j.model.ValidationException; -import net.fortuna.ical4j.util.CompatibilityHints; import net.fortuna.ical4j.vcard.GroupRegistry; import net.fortuna.ical4j.vcard.ParameterFactoryRegistry; import net.fortuna.ical4j.vcard.Property; @@ -33,6 +32,7 @@ import net.fortuna.ical4j.vcard.VCard; import net.fortuna.ical4j.vcard.VCardBuilder; import net.fortuna.ical4j.vcard.VCardOutputter; import net.fortuna.ical4j.vcard.parameter.Type; +import net.fortuna.ical4j.vcard.property.Address; import net.fortuna.ical4j.vcard.property.BDay; import net.fortuna.ical4j.vcard.property.Email; import net.fortuna.ical4j.vcard.property.Fn; @@ -45,7 +45,6 @@ import net.fortuna.ical4j.vcard.property.Uid; import net.fortuna.ical4j.vcard.property.Url; import net.fortuna.ical4j.vcard.property.Version; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import android.util.Log; @@ -64,7 +63,7 @@ public class Contact extends Resource { @Getter @Setter private String prefix, givenName, middleName, familyName, suffix; @Getter @Setter private String phoneticGivenName, phoneticMiddleName, phoneticFamilyName; @Getter @Setter private String[] nickNames; - + @Getter @Setter private byte[] photo; @Getter @Setter private Date birthDay; @@ -92,6 +91,11 @@ public class Contact extends Resource { phoneNumbers.add(number); } + @Getter private List
addresses = new LinkedList
(); + public void addAddress(Address address) { + addresses.add(address); + } + @Getter private List URLs = new LinkedList(); public void addURL(URI url) { URLs.add(url); @@ -118,8 +122,6 @@ public class Contact extends Resource { propertyFactoryRegistry.register("X-" + PhoneticMiddleName.PROPERTY_NAME, new PhoneticMiddleName.Factory()); propertyFactoryRegistry.register("X-" + PhoneticLastName.PROPERTY_NAME, new PhoneticLastName.Factory()); - //Log.d(TAG, IOUtils.toString(is)); - VCardBuilder builder = new VCardBuilder( new InputStreamReader(is), new GroupRegistry(), @@ -155,7 +157,6 @@ public class Contact extends Resource { if (nickname != null) nickNames = nickname.getNames(); - // structured name N n = (N)vcard.getProperty(Id.N); if (n != null) { prefix = StringUtils.join(n.getPrefixes(), " "); @@ -165,7 +166,6 @@ public class Contact extends Resource { suffix = StringUtils.join(n.getSuffixes(), " "); } - // phonetic name PhoneticFirstName phoneticFirstName = (PhoneticFirstName)vcard.getExtendedProperty(PhoneticFirstName.PROPERTY_NAME); if (phoneticFirstName != null) phoneticGivenName = phoneticFirstName.getValue(); @@ -182,6 +182,9 @@ public class Contact extends Resource { for (Property p : vcard.getProperties(Id.TEL)) phoneNumbers.add((Telephone)p); + for (Property p : vcard.getProperties(Id.ADR)) + addresses.add((Address)p); + Photo photo = (Photo)vcard.getProperty(Id.PHOTO); if (photo != null) this.photo = photo.getBinary(); @@ -236,12 +239,15 @@ public class Contact extends Resource { properties.add(new PhoneticMiddleName(phoneticMiddleName)); if (phoneticFamilyName != null) properties.add(new PhoneticLastName(phoneticFamilyName)); + + if (!emails.isEmpty()) + properties.addAll(emails); + + if (!addresses.isEmpty()) + properties.addAll(addresses); - for (Email email : emails) - properties.add(email); - - for (Telephone number : phoneNumbers) - properties.add(number); + if (!phoneNumbers.isEmpty()) + properties.addAll(phoneNumbers); for (URI uri : URLs) properties.add(new Url(uri)); diff --git a/src/at/bitfire/davdroid/resource/LocalAddressBook.java b/src/at/bitfire/davdroid/resource/LocalAddressBook.java index b734603f..1dfdd6da 100644 --- a/src/at/bitfire/davdroid/resource/LocalAddressBook.java +++ b/src/at/bitfire/davdroid/resource/LocalAddressBook.java @@ -16,6 +16,7 @@ import java.util.List; import net.fortuna.ical4j.model.Date; import net.fortuna.ical4j.vcard.Parameter.Id; import net.fortuna.ical4j.vcard.parameter.Type; +import net.fortuna.ical4j.vcard.property.Address; import net.fortuna.ical4j.vcard.property.Telephone; import org.apache.commons.lang.StringUtils; @@ -37,6 +38,7 @@ import android.provider.ContactsContract.CommonDataKinds.Note; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.Photo; import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; @@ -223,6 +225,32 @@ public class LocalAddressBook extends LocalCollection { c.addPhoneNumber(number); } + // postal addresses + cursor = providerClient.query(dataURI(), new String[] { + /* 0 */ StructuredPostal.FORMATTED_ADDRESS, StructuredPostal.TYPE, + /* 2 */ StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD, + /* 5 */ StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE, + /* 8 */ StructuredPostal.COUNTRY + }, StructuredPostal.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), StructuredPostal.CONTENT_ITEM_TYPE }, null); + while (cursor != null && cursor.moveToNext()) { + Type[] types = new Type[] {}; + switch (cursor.getInt(1)) { + case StructuredPostal.TYPE_HOME: + types = new Type[] { Type.HOME }; + break; + case StructuredPostal.TYPE_WORK: + types = new Type[] { Type.WORK }; + break; + } + Address address = new Address( + cursor.getString(3), cursor.getString(4), cursor.getString(2), + cursor.getString(5), cursor.getString(6), cursor.getString(7), + cursor.getString(8), types + ); + c.addAddress(address); + } + // photo cursor = providerClient.query(dataURI(), new String[] { Photo.PHOTO }, Photo.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", @@ -329,6 +357,9 @@ public class LocalAddressBook extends LocalCollection { for (Telephone number : contact.getPhoneNumbers()) pendingOperations.add(buildPhoneNumber(newDataInsertBuilder(localID, backrefIdx), number).build()); + + for (Address address : contact.getAddresses()) + pendingOperations.add(buildAddress(newDataInsertBuilder(localID, backrefIdx), address).build()); if (contact.getPhoto() != null) pendingOperations.add(buildPhoto(newDataInsertBuilder(localID, backrefIdx), contact.getPhoto()).build()); @@ -444,6 +475,43 @@ public class LocalAddressBook extends LocalCollection { .withValue(Phone.TYPE, type); } + protected Builder buildAddress(Builder builder, Address address) { + /* street po.box (extended) + * region + * postal code city + * country + */ + String lineStreet = StringUtils.join(new String[] { address.getStreet(), address.getPoBox(), address.getExtended() }, " "), + lineLocality = StringUtils.join(new String[] { address.getPostcode(), address.getLocality() }, " "); + + List lines = new LinkedList(); + if (lineStreet != null) + lines.add(lineStreet); + if (address.getRegion() != null && !address.getRegion().isEmpty()) + lines.add(address.getRegion()); + if (lineLocality != null) + lines.add(lineLocality); + + int typeCode = StructuredPostal.TYPE_OTHER; + Type type = (Type)address.getParameter(Id.TYPE); + if (type == Type.HOME) + typeCode = StructuredPostal.TYPE_HOME; + else if (type == Type.WORK) + typeCode = StructuredPostal.TYPE_WORK; + + return builder + .withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE) + .withValue(StructuredPostal.FORMATTED_ADDRESS, StringUtils.join(lines, "\n")) + .withValue(StructuredPostal.TYPE, typeCode) + .withValue(StructuredPostal.STREET, address.getStreet()) + .withValue(StructuredPostal.POBOX, address.getPoBox()) + .withValue(StructuredPostal.NEIGHBORHOOD, address.getExtended()) + .withValue(StructuredPostal.CITY, address.getLocality()) + .withValue(StructuredPostal.REGION, address.getRegion()) + .withValue(StructuredPostal.POSTCODE, address.getPostcode()) + .withValue(StructuredPostal.COUNTRY, address.getCountry()); + } + protected Builder buildPhoto(Builder builder, byte[] photo) { return builder .withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE) diff --git a/src/at/bitfire/davdroid/resource/RemoteCollection.java b/src/at/bitfire/davdroid/resource/RemoteCollection.java index 5e5557ea..97243c79 100644 --- a/src/at/bitfire/davdroid/resource/RemoteCollection.java +++ b/src/at/bitfire/davdroid/resource/RemoteCollection.java @@ -8,6 +8,7 @@ package at.bitfire.davdroid.resource; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.LinkedList; @@ -80,8 +81,12 @@ public abstract class RemoteCollection { for (WebDavResource member : collection.getMembers()) { ResourceType resource = newResourceSkeleton(member.getName(), member.getETag()); try { - resource.parseEntity(member.getContent()); - foundResources.add(resource); + InputStream is = member.getContent(); + if (is != null) { + resource.parseEntity(is); + foundResources.add(resource); + } else + Log.e(TAG, "Ignoring entity without content"); } catch (ParserException ex) { Log.e(TAG, "Ignoring unparseable entity in multi-response", ex); } diff --git a/src/at/bitfire/davdroid/webdav/WebDavResource.java b/src/at/bitfire/davdroid/webdav/WebDavResource.java index 062bb7ac..8b405ac0 100644 --- a/src/at/bitfire/davdroid/webdav/WebDavResource.java +++ b/src/at/bitfire/davdroid/webdav/WebDavResource.java @@ -140,12 +140,14 @@ public class WebDavResource { checkResponse(response); Header[] allowHeaders = response.getHeaders("Allow"); - for (Header allowHeader : allowHeaders) - methods.addAll(Arrays.asList(allowHeader.getValue().split(", ?"))); + if (allowHeaders != null) + for (Header allowHeader : allowHeaders) + methods.addAll(Arrays.asList(allowHeader.getValue().split(", ?"))); Header[] capHeaders = response.getHeaders("DAV"); - for (Header capHeader : capHeaders) - capabilities.addAll(Arrays.asList(capHeader.getValue().split(", ?"))); + if (capHeaders != null) + for (Header capHeader : capHeaders) + capabilities.addAll(Arrays.asList(capHeader.getValue().split(", ?"))); } public boolean supportsDAV(String capability) {