Support relations the VCard 4.0 way (closes #278)

pull/2/head
Ricki Hirner 9 years ago
parent b5c99265c3
commit f1eabb6227

@ -18,8 +18,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import at.bitfire.davdroid.Constants;
@ -27,8 +30,10 @@ import ezvcard.Ezvcard;
import ezvcard.VCard;
import ezvcard.VCardVersion;
import ezvcard.ValidationWarnings;
import ezvcard.Warning;
import ezvcard.parameter.EmailType;
import ezvcard.parameter.ImageType;
import ezvcard.parameter.RelatedType;
import ezvcard.parameter.TelephoneType;
import ezvcard.property.Address;
import ezvcard.property.Anniversary;
@ -44,6 +49,7 @@ import ezvcard.property.Organization;
import ezvcard.property.Photo;
import ezvcard.property.ProductId;
import ezvcard.property.RawProperty;
import ezvcard.property.Related;
import ezvcard.property.Revision;
import ezvcard.property.Role;
import ezvcard.property.Sound;
@ -53,6 +59,8 @@ import ezvcard.property.Telephone;
import ezvcard.property.Title;
import ezvcard.property.Uid;
import ezvcard.property.Url;
import ezvcard.property.VCardProperty;
import ezvcard.util.ListMultimap;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ -83,6 +91,13 @@ public class Contact extends Resource {
PHONE_TYPE_RADIO = TelephoneType.get("X-RADIO"),
PHONE_TYPE_ASSISTANT = TelephoneType.get("X-ASSISTANT"),
PHONE_TYPE_MMS = TelephoneType.get("X-MMS");
public final static RelatedType
RELATED_TYPE_BROTHER = RelatedType.get("brother"),
RELATED_TYPE_FATHER = RelatedType.get("father"),
RELATED_TYPE_MANAGER = RelatedType.get("manager"),
RELATED_TYPE_MOTHER = RelatedType.get("mother"),
RELATED_TYPE_REFERRED_BY = RelatedType.get("referred-by"),
RELATED_TYPE_SISTER = RelatedType.get("sister");
@Getter @Setter private String unknownProperties;
@ -100,13 +115,13 @@ public class Contact extends Resource {
@Getter @Setter private Anniversary anniversary;
@Getter @Setter private Birthday birthDay;
@Getter private List<Telephone> phoneNumbers = new LinkedList<Telephone>();
@Getter private List<Email> emails = new LinkedList<Email>();
@Getter private List<Impp> impps = new LinkedList<Impp>();
@Getter private List<Address> addresses = new LinkedList<Address>();
@Getter private List<String> categories = new LinkedList<String>();
@Getter private List<String> URLs = new LinkedList<String>();
@Getter private List<Telephone> phoneNumbers = new LinkedList<>();
@Getter private List<Email> emails = new LinkedList<>();
@Getter private List<Impp> impps = new LinkedList<>();
@Getter private List<Address> addresses = new LinkedList<>();
@Getter private List<String> categories = new LinkedList<>();
@Getter private List<String> URLs = new LinkedList<>();
@Getter private List<Related> relations = new LinkedList<>();
/* instance methods */
@ -278,7 +293,17 @@ public class Contact extends Resource {
// ANNIVERSARY
anniversary = vcard.getAnniversary();
vcard.removeProperties(Anniversary.class);
// RELATED
for (Related related : vcard.getRelations()) {
String text = related.getText();
if (!StringUtils.isNotEmpty(text)) {
// process only free-form relations with text
relations.add(related);
vcard.removeProperty(related);
}
}
// X-SIP
for (RawProperty sip : vcard.getExtendedProperties(PROPERTY_SIP))
impps.add(new Impp("sip", sip.getValue()));
@ -411,6 +436,9 @@ public class Contact extends Resource {
// PHOTO
if (photo != null)
vcard.addPhoto(new Photo(photo, ImageType.JPEG));
for (Related related : relations)
vcard.addRelated(related);
// PRODID, REV
vcard.setProductId("DAVdroid/" + Constants.APP_VERSION + " (ez-vcard/" + Ezvcard.VERSION + ")");
@ -419,8 +447,8 @@ public class Contact extends Resource {
// validate and print warnings
ValidationWarnings warnings = vcard.validate(vCardVersion);
if (!warnings.isEmpty())
Log.w(TAG, "Created potentially invalid VCard! " + warnings);
Log.w(TAG, "Created potentially invalid VCard:\n" + warnings);
ByteArrayOutputStream os = new ByteArrayOutputStream();
Ezvcard
.write(vcard)

@ -27,6 +27,7 @@ import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
@ -45,22 +46,27 @@ import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import at.bitfire.davdroid.syncadapter.AccountSettings;
import ezvcard.parameter.AddressType;
import ezvcard.parameter.EmailType;
import ezvcard.parameter.ImppType;
import ezvcard.parameter.RelatedType;
import ezvcard.parameter.TelephoneType;
import ezvcard.property.Address;
import ezvcard.property.Anniversary;
import ezvcard.property.Birthday;
import ezvcard.property.DateOrTimeProperty;
import ezvcard.property.Impp;
import ezvcard.property.Related;
import ezvcard.property.Telephone;
import lombok.Cleanup;
@ -201,6 +207,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
populateCategories(c);
populateURLs(c);
populateEvents(c);
populateRelations(c);
populateSipAddress(c);
} catch(RemoteException ex) {
throw new LocalStorageException(ex);
@ -573,6 +580,80 @@ public class LocalAddressBook extends LocalCollection<Contact> {
}
}
}
protected void populateRelations(Contact c) throws RemoteException {
@Cleanup Cursor cursor = providerClient.query(dataURI(),
new String[] { Relation.NAME, Relation.TYPE, Relation.LABEL },
Relation.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
new String[] { String.valueOf(c.getLocalID()), Relation.CONTENT_ITEM_TYPE }, null);
Map<String, List<RelatedType>> relations = new HashMap<>();
while (cursor != null && cursor.moveToNext()) {
String name = cursor.getString(0);
List<RelatedType> types = relations.get(name);
if (types == null) {
types = new LinkedList<>();
relations.put(name, types);
}
switch (cursor.getInt(1)) {
case Relation.TYPE_ASSISTANT:
types.add(RelatedType.AGENT);
break;
case Relation.TYPE_BROTHER:
types.add(RelatedType.SIBLING);
types.add(Contact.RELATED_TYPE_BROTHER);
break;
case Relation.TYPE_CHILD:
types.add(RelatedType.CHILD);
break;
case Relation.TYPE_DOMESTIC_PARTNER:
types.add(RelatedType.CO_RESIDENT);
break;
case Relation.TYPE_FATHER:
types.add(Contact.RELATED_TYPE_FATHER);
break;
case Relation.TYPE_FRIEND:
types.add(RelatedType.FRIEND);
break;
case Relation.TYPE_MANAGER:
types.add(Contact.RELATED_TYPE_MANAGER);
break;
case Relation.TYPE_MOTHER:
types.add(Contact.RELATED_TYPE_MOTHER);
break;
case Relation.TYPE_PARENT:
types.add(RelatedType.PARENT);
break;
case Relation.TYPE_PARTNER:
types.add(RelatedType.SWEETHEART);
break;
case Relation.TYPE_REFERRED_BY:
types.add(Contact.RELATED_TYPE_REFERRED_BY);
case Relation.TYPE_RELATIVE:
types.add(RelatedType.KIN);
break;
case Relation.TYPE_SISTER:
types.add(RelatedType.SIBLING);
types.add(Contact.RELATED_TYPE_SISTER);
break;
case Relation.TYPE_SPOUSE:
types.add(RelatedType.SPOUSE);
case Relation.TYPE_CUSTOM:
String customType = cursor.getString(2);
if (!StringUtils.isEmpty(customType))
types.add(RelatedType.get(customType));
}
}
for (String name : relations.keySet()) {
Related related = new Related();
related.setText(name);
for (RelatedType type : relations.get(name))
related.addType(type);
c.getRelations().add(related);
}
}
protected void populateSipAddress(Contact c) throws RemoteException {
@Cleanup Cursor cursor = providerClient.query(dataURI(),
@ -659,9 +740,11 @@ public class LocalAddressBook extends LocalCollection<Contact> {
queueOperation(buildEvent(newDataInsertBuilder(localID, backrefIdx), contact.getAnniversary(), CommonDataKinds.Event.TYPE_ANNIVERSARY));
if (contact.getBirthDay() != null)
queueOperation(buildEvent(newDataInsertBuilder(localID, backrefIdx), contact.getBirthDay(), CommonDataKinds.Event.TYPE_BIRTHDAY));
// TODO relations
for (Related related : contact.getRelations())
for (RelatedType type : related.getTypes())
queueOperation(buildRelated(newDataInsertBuilder(localID, backrefIdx), type, related.getText()));
// SIP addresses are built by buildIMPP
}
@ -981,7 +1064,35 @@ public class LocalAddressBook extends LocalCollection<Contact> {
.withValue(CommonDataKinds.Event.TYPE, type)
.withValue(CommonDataKinds.Event.START_DATE, formatter.format(date.getDate()));
}
protected Builder buildRelated(Builder builder, RelatedType type, String name) {
int typeCode = 0;
String typeLabel = null;
if (type == RelatedType.CHILD)
typeCode = Relation.TYPE_CHILD;
else if (type == RelatedType.CO_RESIDENT)
typeCode = Relation.TYPE_DOMESTIC_PARTNER;
else if (type == RelatedType.FRIEND)
typeCode = Relation.TYPE_FRIEND;
else if (type == RelatedType.PARENT)
typeCode = Relation.TYPE_PARENT;
else if (type == RelatedType.SPOUSE)
typeCode = Relation.TYPE_SPOUSE;
else if (type == RelatedType.KIN)
typeCode = Relation.TYPE_RELATIVE;
else if (type == RelatedType.SWEETHEART)
typeCode = Relation.TYPE_PARTNER;
else {
typeCode = Relation.TYPE_CUSTOM;
typeLabel = type.getValue();
}
return builder
.withValue(Data.MIMETYPE, Relation.CONTENT_ITEM_TYPE)
.withValue(Relation.TYPE, typeCode)
.withValue(Relation.NAME, name)
.withValue(Relation.LABEL, typeLabel);
}
/* helper methods */

@ -87,6 +87,24 @@
<Type maxOccurs="1" type="birthday" yearOptional="false" />
<Type maxOccurs="1" type="anniversary" />
</DataKind>
<DataKind kind="relationship">
<Type type="assistant"/>
<Type type="brother"/>
<Type type="child"/>
<Type type="domestic_partner"/>
<Type type="father"/>
<Type type="friend"/>
<Type type="manager"/>
<Type type="mother"/>
<Type type="parent"/>
<Type type="partner"/>
<Type type="referred_by"/>
<Type type="relative"/>
<Type type="sister"/>
<Type type="spouse"/>
<Type type="custom"/>
</DataKind>
<DataKind kind="sip_address" maxOccurs="1" />
</EditSchema>

Loading…
Cancel
Save