org.anhonesteffort.flock.sync.addressbook.ContactFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.anhonesteffort.flock.sync.addressbook.ContactFactory.java

Source

/*
 * *
 *  Copyright (C) 2014 Open Whisper Systems
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 * /
 */

package org.anhonesteffort.flock.sync.addressbook;

import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.util.Log;

import com.google.common.base.Optional;
import ezvcard.VCard;
import ezvcard.VCardVersion;
import ezvcard.parameter.AddressType;
import ezvcard.parameter.EmailType;
import ezvcard.parameter.ImageType;
import ezvcard.parameter.ImppType;
import ezvcard.parameter.TelephoneType;
import ezvcard.property.Address;
import ezvcard.property.Birthday;
import ezvcard.property.Email;
import ezvcard.property.FormattedName;
import ezvcard.property.Impp;
import ezvcard.property.Nickname;
import ezvcard.property.Note;
import ezvcard.property.Organization;
import ezvcard.property.Photo;
import ezvcard.property.RawProperty;
import ezvcard.property.Role;
import ezvcard.property.StructuredName;
import ezvcard.property.Telephone;
import ezvcard.property.Uid;
import ezvcard.property.Url;
import ezvcard.util.IOUtils;

import org.anhonesteffort.flock.webdav.carddav.CardDavConstants;
import org.anhonesteffort.flock.webdav.ComponentETagPair;
import org.anhonesteffort.flock.webdav.InvalidComponentException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

/**
 * Programmer: rhodey
 *
 * Much thanks to the DAVDroid project and especially
 * Richard Hirner (bitfire web engineering) for leading
 * the way in shoving VCard objects into Androids Contacts
 * Content Provider. This would have been much more of a
 * pain without a couple hints from the DAVDroid codebase.
 */
public class ContactFactory {

    private static final String TAG = "org.anhonesteffort.flock.sync.addressbook.ContactFactory";

    private static final String PROPERTY_PHONETIC_GIVEN_NAME = "X-PHONETIC-GIVEN-NAME";
    private static final String PROPERTY_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
    private static final String PROPERTY_PHONETIC_FAMILY_NAME = "X-PHONETIC-FAMILY-NAME";

    private static final EmailType EMAIL_TYPE_MOBILE = EmailType.get("X-MOBILE");

    private static final String PROPERTY_SIP = "X-SIP";
    private static final String PROPERTY_STARRED = "X-STARRED";
    private static final String PROPERTY_EVENT_ANNIVERSARY = "X-EVENT-ANNIVERSARY";
    private static final String PROPERTY_EVENT_OTHER = "X-EVENT-OTHER";
    private static final String PROPERTY_EVENT_CUSTOM = "X-EVENT-CUSTOM";
    private static final String PARAMETER_EVENT_CUSTOM_LABEL = "X-EVENT-CUSTOM-LABEL";

    private static String propertyNameToLabel(String propertyName) {
        return WordUtils.capitalize(propertyName.toLowerCase().replace("x-", "").replace("_", " "));
    }

    protected static String labelToPropertyName(String label) {
        return "X-" + label.replace(" ", "_").toUpperCase();
    }

    protected static String[] getProjectionForRawContact() {
        return new String[] { ContactsContract.RawContacts._ID, ContactsContract.RawContacts.SOURCE_ID, // UID
                ContactsContract.RawContacts.SYNC1, // ETAG
                ContactsContract.RawContacts.STARRED };
    }

    protected static ContentValues getValuesForRawContact(Cursor cursor) {
        ContentValues values = new ContentValues(4);

        values.put(ContactsContract.RawContacts._ID, cursor.getLong(0));
        values.put(ContactsContract.RawContacts.SOURCE_ID, cursor.getString(1));
        values.put(ContactsContract.RawContacts.SYNC1, cursor.getString(2));
        values.put(ContactsContract.RawContacts.STARRED, (cursor.getInt(3) != 0));

        return values;
    }

    protected static ContentValues getValuesForRawContact(ComponentETagPair<VCard> vCard) {
        ContentValues values = new ContentValues();
        Uid uid = vCard.getComponent().getUid();

        if (uid != null)
            values.put(ContactsContract.RawContacts.SOURCE_ID, uid.getValue());

        if (vCard.getETag().isPresent())
            values.put(ContactsContract.RawContacts.SYNC1, vCard.getETag().get());

        RawProperty starredProp = vCard.getComponent().getExtendedProperty(PROPERTY_STARRED);
        if (starredProp != null) {
            boolean is_starred = Integer.parseInt(starredProp.getValue()) != 0;
            values.put(ContactsContract.RawContacts.STARRED, is_starred);
        }

        return values;
    }

    protected static ComponentETagPair<VCard> getVCard(ContentValues rawContactValues) {
        String uidText = rawContactValues.getAsString(ContactsContract.RawContacts.SOURCE_ID);
        String eTagText = rawContactValues.getAsString(ContactsContract.RawContacts.SYNC1);
        Boolean starred = rawContactValues.getAsBoolean(ContactsContract.RawContacts.STARRED);

        VCard vCard = new VCard();
        vCard.setVersion(VCardVersion.V3_0);

        vCard.setUid(new Uid(uidText));

        if (starred != null)
            vCard.setExtendedProperty(PROPERTY_STARRED, starred ? "1" : "0");

        Optional<String> eTag = Optional.fromNullable(eTagText);

        return new ComponentETagPair<VCard>(vCard, eTag);
    }

    protected static String[] getProjectionForStructuredName() {
        return new String[] { ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, // 00
                ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, // 01
                ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, // 02
                ContactsContract.CommonDataKinds.StructuredName.PREFIX, // 03
                ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, // 04
                ContactsContract.CommonDataKinds.StructuredName.SUFFIX, // 05
                ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME, // 06
                ContactsContract.CommonDataKinds.StructuredName.PHONETIC_MIDDLE_NAME, // 07
                ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME // 08
        };
    }

    protected static ContentValues getValuesForStructuredName(Cursor cursor) {
        ContentValues values = new ContentValues(9);

        values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, cursor.getString(0));
        values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, cursor.getString(1));
        values.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, cursor.getString(2));
        values.put(ContactsContract.CommonDataKinds.StructuredName.PREFIX, cursor.getString(3));
        values.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, cursor.getString(4));
        values.put(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, cursor.getString(5));
        values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME, cursor.getString(6));
        values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_MIDDLE_NAME, cursor.getString(7));
        values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME, cursor.getString(8));

        return values;
    }

    protected static Optional<ContentValues> getValuesForStructuredName(VCard vCard) {
        if (vCard.getStructuredName() != null) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);

            FormattedName formattedName = vCard.getFormattedName();
            if (formattedName != null)
                values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, formattedName.getValue());

            StructuredName structuredName = vCard.getStructuredName();
            if (structuredName != null) {
                values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, structuredName.getGiven());
                values.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, structuredName.getFamily());

                if (structuredName.getPrefixes().size() > 0)
                    values.put(ContactsContract.CommonDataKinds.StructuredName.PREFIX,
                            structuredName.getPrefixes().toString());

                if (structuredName.getAdditional().size() > 0)
                    values.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
                            StringUtils.join(structuredName.getAdditional(), " "));

                if (structuredName.getSuffixes().size() > 0)
                    values.put(ContactsContract.CommonDataKinds.StructuredName.SUFFIX,
                            structuredName.getSuffixes().toString());
            }

            RawProperty phoneticGivenName = vCard.getExtendedProperty(PROPERTY_PHONETIC_GIVEN_NAME);
            if (phoneticGivenName != null)
                values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME,
                        phoneticGivenName.getValue());

            RawProperty phoneticMiddleName = vCard.getExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME);
            if (phoneticMiddleName != null)
                values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_MIDDLE_NAME,
                        phoneticMiddleName.getValue());

            RawProperty phoneticFamilyName = vCard.getExtendedProperty(PROPERTY_PHONETIC_FAMILY_NAME);
            if (phoneticFamilyName != null)
                values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME,
                        phoneticFamilyName.getValue());

            return Optional.of(values);
        }

        // TODO: handle case where only email address is present
        Log.w(TAG, "structured name missing, returning absent");
        return Optional.absent();
    }

    protected static void addStructuredName(VCard vCard, ContentValues structuredNameValues) {
        String displayName = structuredNameValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
        String givenName = structuredNameValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
        String familyName = structuredNameValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
        String prefix = structuredNameValues.getAsString(ContactsContract.CommonDataKinds.StructuredName.PREFIX);
        String middleName = structuredNameValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME);
        String suffix = structuredNameValues.getAsString(ContactsContract.CommonDataKinds.StructuredName.SUFFIX);
        String phoneticGivenName = structuredNameValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME);
        String phoneticMiddleName = structuredNameValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_MIDDLE_NAME);
        String phoneticFamilyName = structuredNameValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME);

        if (displayName != null) {
            FormattedName formattedName = new FormattedName(displayName);
            vCard.addFormattedName(formattedName);

            StructuredName structuredName = new StructuredName();

            if (givenName != null)
                structuredName.setGiven(givenName);

            if (familyName != null)
                structuredName.setFamily(familyName);

            if (prefix != null)
                structuredName.addPrefix(prefix);

            if (middleName != null)
                structuredName.addAdditional(middleName);

            if (suffix != null)
                structuredName.addSuffix(suffix);

            vCard.setStructuredName(structuredName);

            if (phoneticGivenName != null)
                vCard.addExtendedProperty(PROPERTY_PHONETIC_GIVEN_NAME, phoneticGivenName);

            if (phoneticMiddleName != null)
                vCard.addExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME, phoneticMiddleName);

            if (phoneticFamilyName != null)
                vCard.addExtendedProperty(PROPERTY_PHONETIC_FAMILY_NAME, phoneticFamilyName);
        } else
            Log.w(TAG, "display name missing, nothing to add");
    }

    protected static String[] getProjectionForPhoneNumber() {
        return new String[] { ContactsContract.CommonDataKinds.Phone.TYPE, // 00
                ContactsContract.CommonDataKinds.Phone.LABEL, // 01
                ContactsContract.CommonDataKinds.Phone.NUMBER, // 02
                ContactsContract.CommonDataKinds.Phone.IS_PRIMARY, // 03
                ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY // 04
        };
    }

    protected static ContentValues getValuesForPhoneNumber(Cursor cursor) {
        ContentValues values = new ContentValues(5);

        values.put(ContactsContract.CommonDataKinds.Phone.TYPE, cursor.getInt(0));
        values.put(ContactsContract.CommonDataKinds.Phone.LABEL, cursor.getString(1));
        values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, cursor.getString(2));
        values.put(ContactsContract.CommonDataKinds.Phone.IS_PRIMARY, (cursor.getInt(3) != 0));
        values.put(ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY, (cursor.getInt(4) != 0));

        return values;
    }

    protected static List<ContentValues> getValuesForPhoneNumbers(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();
        List<Telephone> telephones = vCard.getTelephoneNumbers();

        for (Telephone telephone : telephones) {
            ContentValues values = new ContentValues();
            int phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_OTHER;

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);

            if (telephone.getTypes().contains(TelephoneType.FAX)) {
                if (telephone.getTypes().contains(TelephoneType.HOME))
                    phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME;
                else if (telephone.getTypes().contains(TelephoneType.WORK))
                    phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK;
                else
                    phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX;
            } else if (telephone.getTypes().contains(TelephoneType.CELL))
                phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;
            else if (telephone.getTypes().contains(TelephoneType.PAGER))
                phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_PAGER;
            else if (telephone.getTypes().contains(TelephoneType.WORK))
                phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_WORK;
            else if (telephone.getTypes().contains(TelephoneType.HOME))
                phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_HOME;
            else if (telephone.getTypes().contains(TelephoneType.PREF))
                phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_MAIN;
            else if (!telephone.getTypes().isEmpty()) {
                phone_type = ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM;
                values.put(ContactsContract.CommonDataKinds.Phone.LABEL,
                        propertyNameToLabel(telephone.getTypes().iterator().next().getValue()));
            }

            values.put(ContactsContract.CommonDataKinds.Phone.TYPE, phone_type);
            values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, telephone.getText());

            values.put(ContactsContract.CommonDataKinds.Phone.IS_PRIMARY,
                    telephone.getTypes().contains(TelephoneType.PREF) ? 1 : 0);
            values.put(ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY,
                    telephone.getTypes().contains(TelephoneType.PREF) ? 1 : 0);

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addPhoneNumber(String path, VCard vCard, ContentValues phoneNumberValues)
            throws InvalidComponentException {
        Integer type = phoneNumberValues.getAsInteger(ContactsContract.CommonDataKinds.Phone.TYPE);
        String label = phoneNumberValues.getAsString(ContactsContract.CommonDataKinds.Phone.LABEL);
        String number = phoneNumberValues.getAsString(ContactsContract.CommonDataKinds.Phone.NUMBER);
        Boolean isPrimary = phoneNumberValues.getAsBoolean(ContactsContract.CommonDataKinds.Phone.IS_PRIMARY);
        Boolean isSuperPrimary = phoneNumberValues
                .getAsBoolean(ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY);

        if (type != null && number != null) {
            Telephone telephone = new Telephone(number);

            switch (type) {
            case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
                telephone.addType(TelephoneType.CELL);
                break;

            case ContactsContract.CommonDataKinds.Phone.TYPE_WORK:
                telephone.addType(TelephoneType.WORK);
                break;

            case ContactsContract.CommonDataKinds.Phone.TYPE_HOME:
                telephone.addType(TelephoneType.HOME);
                break;

            case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK:
                telephone.addType(TelephoneType.FAX);
                telephone.addType(TelephoneType.WORK);
                break;

            case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME:
                telephone.addType(TelephoneType.FAX);
                telephone.addType(TelephoneType.HOME);
                break;

            case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER:
                telephone.addType(TelephoneType.PAGER);
                break;

            case ContactsContract.CommonDataKinds.Phone.TYPE_MAIN:
                telephone.addType(TelephoneType.PREF);
                break;

            default:
                if (label != null)
                    telephone.addType(TelephoneType.get(labelToPropertyName(label)));
            }

            if (isPrimary != null && isPrimary)
                telephone.addType(TelephoneType.PREF);
            else if (isSuperPrimary != null && isSuperPrimary)
                telephone.addType(TelephoneType.PREF);

            vCard.addTelephoneNumber(telephone);
        } else {
            Log.e(TAG, "phone type or number is null, not adding anything");
            throw new InvalidComponentException("phone type or number is null", false,
                    CardDavConstants.CARDDAV_NAMESPACE, path);
        }
    }

    protected static String[] getProjectionForEmailAddress() {
        return new String[] { ContactsContract.CommonDataKinds.Email.TYPE, // 00
                ContactsContract.CommonDataKinds.Email.ADDRESS, // 01
                ContactsContract.CommonDataKinds.Email.LABEL, // 02
                ContactsContract.CommonDataKinds.Email.IS_PRIMARY, // 03
                ContactsContract.CommonDataKinds.Email.IS_SUPER_PRIMARY // 04
        };
    }

    protected static ContentValues getValuesForEmailAddress(Cursor cursor) {
        ContentValues values = new ContentValues(5);

        values.put(ContactsContract.CommonDataKinds.Email.TYPE, cursor.getInt(0));
        values.put(ContactsContract.CommonDataKinds.Email.ADDRESS, cursor.getString(1));
        values.put(ContactsContract.CommonDataKinds.Email.LABEL, cursor.getString(2));
        values.put(ContactsContract.CommonDataKinds.Email.IS_PRIMARY, (cursor.getInt(3) != 0));
        values.put(ContactsContract.CommonDataKinds.Email.IS_SUPER_PRIMARY, (cursor.getInt(4) != 0));

        return values;
    }

    protected static List<ContentValues> getValuesForEmailAddresses(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();
        List<Email> emails = vCard.getEmails();

        for (Email email : emails) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);

            if (email.getTypes().contains(EmailType.HOME))
                values.put(ContactsContract.CommonDataKinds.Email.TYPE,
                        ContactsContract.CommonDataKinds.Email.TYPE_HOME);
            else if (email.getTypes().contains(EmailType.WORK))
                values.put(ContactsContract.CommonDataKinds.Email.TYPE,
                        ContactsContract.CommonDataKinds.Email.TYPE_WORK);
            else if (email.getTypes().contains(EMAIL_TYPE_MOBILE))
                values.put(ContactsContract.CommonDataKinds.Email.TYPE,
                        ContactsContract.CommonDataKinds.Email.TYPE_MOBILE);
            else if (!email.getTypes().isEmpty()) {
                values.put(ContactsContract.CommonDataKinds.Email.TYPE,
                        ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM);
                values.put(ContactsContract.CommonDataKinds.Email.LABEL,
                        propertyNameToLabel(email.getTypes().iterator().next().getValue()));
            } else
                values.put(ContactsContract.CommonDataKinds.Email.TYPE,
                        ContactsContract.CommonDataKinds.Email.TYPE_OTHER);

            values.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email.getValue());

            values.put(ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
                    email.getTypes().contains(EmailType.PREF) ? 1 : 0);
            values.put(ContactsContract.CommonDataKinds.Email.IS_SUPER_PRIMARY,
                    email.getTypes().contains(EmailType.PREF) ? 1 : 0);

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addEmailAddress(String path, VCard vCard, ContentValues emailValues)
            throws InvalidComponentException {
        Integer type = emailValues.getAsInteger(ContactsContract.CommonDataKinds.Email.TYPE);
        String label = emailValues.getAsString(ContactsContract.CommonDataKinds.Email.LABEL);
        String address = emailValues.getAsString(ContactsContract.CommonDataKinds.Email.ADDRESS);
        Boolean isPrimary = emailValues.getAsBoolean(ContactsContract.CommonDataKinds.Email.IS_PRIMARY);
        Boolean isSuperPrimary = emailValues.getAsBoolean(ContactsContract.CommonDataKinds.Email.IS_SUPER_PRIMARY);

        if (type != null && address != null) {
            Email email = new Email(address);

            switch (type) {
            case ContactsContract.CommonDataKinds.Email.TYPE_HOME:
                email.addType(EmailType.HOME);
                break;

            case ContactsContract.CommonDataKinds.Email.TYPE_WORK:
                email.addType(EmailType.WORK);
                break;

            case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE:
                email.addType(EMAIL_TYPE_MOBILE);
                break;

            default:
                if (label != null)
                    email.addType(EmailType.get(label));
                break;
            }

            if (isPrimary != null && isPrimary)
                email.addType(EmailType.PREF);
            else if (isSuperPrimary != null && isSuperPrimary)
                email.addType(EmailType.PREF);

            vCard.addEmail(email);
        } else {
            Log.e(TAG, "email type or address is null, not adding anything");
            throw new InvalidComponentException("email type or address is null", false,
                    CardDavConstants.CARDDAV_NAMESPACE, path);
        }
    }

    protected static String[] getProjectionForPicture() {
        return new String[] { ContactsContract.CommonDataKinds.Photo.RAW_CONTACT_ID, // 00
                ContactsContract.CommonDataKinds.Photo.SYNC1 // 01 (image type)
        };
    }

    protected static ContentValues getValuesForPicture(Cursor cursor) {
        ContentValues values = new ContentValues(2);

        values.put(ContactsContract.CommonDataKinds.Photo.RAW_CONTACT_ID, cursor.getLong(0));
        values.put(ContactsContract.CommonDataKinds.Photo.SYNC1, cursor.getString(1));

        return values;
    }

    protected static Optional<ContentValues> getValuesForPicture(VCard vCard) {
        if (vCard.getPhotos().size() > 0) {
            try {

                ContentValues values = new ContentValues();

                ByteArrayOutputStream out = new ByteArrayOutputStream();
                out.write(vCard.getPhotos().get(0).getData());

                values.put(ContactsContract.Data.MIMETYPE,
                        ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
                values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, vCard.getPhotos().get(0).getData());

                return Optional.of(values);

            } catch (IOException e) {
                Log.e(TAG, "caught exception while shoving photo into content values", e);
            }
        } else
            Log.w(TAG, "no photos found in vcard, returning absent");

        return Optional.absent();
    }

    protected static void addPicture(String path, ContentProviderClient client, Uri pictureUri, VCard vCard)
            throws InvalidComponentException, RemoteException {
        try {

            AssetFileDescriptor fileDescriptor = client.openAssetFile(pictureUri, "r");
            InputStream inputStream = fileDescriptor.createInputStream();

            Photo photo = new Photo(IOUtils.toByteArray(inputStream), ImageType.JPEG);
            vCard.addPhoto(photo);

        } catch (FileNotFoundException e) {
            // nothing to do...
        } catch (IOException e) {
            throw new InvalidComponentException("caught exception while adding picture", false,
                    CardDavConstants.CARDDAV_NAMESPACE, path);
        }
    }

    protected static String[] getProjectionForOrganization() {
        return new String[] { ContactsContract.CommonDataKinds.Organization.COMPANY, // 00
                ContactsContract.CommonDataKinds.Organization.TITLE // 01
        };
    }

    protected static ContentValues getValuesForOrganization(Cursor cursor) {
        ContentValues values = new ContentValues(2);

        values.put(ContactsContract.CommonDataKinds.Organization.COMPANY, cursor.getString(0));
        values.put(ContactsContract.CommonDataKinds.Organization.TITLE, cursor.getString(1));

        return values;
    }

    protected static List<ContentValues> getValuesForOrganization(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        for (int i = 0; i < vCard.getOrganizations().size(); i++) {
            Organization organization = vCard.getOrganizations().get(i);
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);

            values.put(ContactsContract.CommonDataKinds.Organization.COMPANY, organization.getValues().get(0));

            if (vCard.getRoles().size() > i)
                values.put(ContactsContract.CommonDataKinds.Organization.TITLE, vCard.getRoles().get(i).getValue());

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addOrganizer(VCard vCard, ContentValues organizerValues) {
        String companyText = organizerValues.getAsString(ContactsContract.CommonDataKinds.Organization.COMPANY);
        String roleText = organizerValues.getAsString(ContactsContract.CommonDataKinds.Organization.TITLE);

        if (companyText != null) {
            Organization organization = new Organization();
            organization.addValue(companyText);
            vCard.addOrganization(organization);
        }

        if (roleText != null) {
            Role role = new Role(roleText);
            vCard.addRole(role);
        }
    }

    protected static String[] getProjectionForInstantMessaging() {
        return new String[] { ContactsContract.CommonDataKinds.Im.TYPE, // 00
                ContactsContract.CommonDataKinds.Im.DATA, // 01
                ContactsContract.CommonDataKinds.Im.LABEL, // 02
                ContactsContract.CommonDataKinds.Im.PROTOCOL, // 03
                ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL // 04
        };
    }

    protected static ContentValues getValuesForInstantMessaging(Cursor cursor) {
        ContentValues values = new ContentValues(5);

        values.put(ContactsContract.CommonDataKinds.Im.TYPE, cursor.getInt(0));
        values.put(ContactsContract.CommonDataKinds.Im.DATA, cursor.getString(1));
        values.put(ContactsContract.CommonDataKinds.Im.LABEL, cursor.getString(2));
        values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, cursor.getInt(3));
        values.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, cursor.getString(4));

        return values;
    }

    protected static List<ContentValues> getValuesForInstantMessaging(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        for (Impp messenger : vCard.getImpps()) {
            if (messenger.getProtocol() == null || messenger.getProtocol().equalsIgnoreCase("sip"))
                break;

            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Im.TYPE, ContactsContract.CommonDataKinds.Im.TYPE_OTHER);

            values.put(ContactsContract.CommonDataKinds.Im.DATA, messenger.getHandle());

            if (messenger.isAim())
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM);
            else if (messenger.isMsn())
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN);
            else if (messenger.isYahoo())
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO);
            else if (messenger.isSkype())
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE);
            else if (messenger.isIcq())
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ);
            else if (messenger.isXmpp())
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER);
            else if (messenger.getProtocol().equalsIgnoreCase("jabber"))
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER);
            else if (messenger.getProtocol().equalsIgnoreCase("qq"))
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ);
            else if (messenger.getProtocol().equalsIgnoreCase("google-talk"))
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK);
            else if (messenger.getProtocol().equalsIgnoreCase("netmeeting"))
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_NETMEETING);
            else {
                values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL,
                        ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM);
                values.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, messenger.getProtocol());
            }

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addInstantMessaging(String path, VCard vCard, ContentValues imValues)
            throws InvalidComponentException {
        Integer type = imValues.getAsInteger(ContactsContract.CommonDataKinds.Im.TYPE);
        Integer protocol = imValues.getAsInteger(ContactsContract.CommonDataKinds.Im.PROTOCOL);
        String customProtocol = imValues.getAsString(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL);
        String handle = imValues.getAsString(ContactsContract.CommonDataKinds.Im.DATA);

        if (type != null && protocol != null && handle != null) {
            Impp impp;

            switch (protocol) {
            case ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM:
                impp = Impp.aim(handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN:
                impp = Impp.msn(handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO:
                impp = Impp.yahoo(handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE:
                impp = Impp.skype(handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ:
                impp = Impp.icq(handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ:
                impp = new Impp("qq", handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK:
                impp = new Impp("google-talk", handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_NETMEETING:
                impp = new Impp("netmeeting", handle);
                break;

            case ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM:
                impp = new Impp(customProtocol, handle);
                break;

            default:
                impp = Impp.xmpp(handle);
                break;
            }

            impp.addType(ImppType.PERSONAL);

            vCard.addImpp(impp);
        } else {
            Log.e(TAG, "im type, protocol, or handle is null, not adding anything");
            throw new InvalidComponentException("im type, protocol, or handle is null", false,
                    CardDavConstants.CARDDAV_NAMESPACE, path);
        }
    }

    protected static String[] getProjectionForNickName() {
        return new String[] { ContactsContract.CommonDataKinds.Nickname.NAME // 00
        };
    }

    protected static ContentValues getValuesForNickName(Cursor cursor) {
        ContentValues values = new ContentValues(1);

        values.put(ContactsContract.CommonDataKinds.Nickname.NAME, cursor.getString(0));

        return values;
    }

    protected static List<ContentValues> getValuesForNickName(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        for (Nickname nickname : vCard.getNicknames()) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Nickname.NAME, nickname.getValues().get(0));

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addNickName(VCard vCard, ContentValues nickNameValues) {
        String nickNameText = nickNameValues.getAsString(ContactsContract.CommonDataKinds.Nickname.NAME);

        if (nickNameText != null) {
            Nickname nickname = new Nickname();
            nickname.addValue(nickNameText);

            vCard.addNickname(nickname);
        }
    }

    protected static String[] getProjectionForNote() {
        return new String[] { ContactsContract.CommonDataKinds.Note.NOTE // 00
        };
    }

    protected static ContentValues getValuesForNote(Cursor cursor) {
        ContentValues values = new ContentValues(1);

        values.put(ContactsContract.CommonDataKinds.Note.NOTE, cursor.getString(0));

        return values;
    }

    protected static List<ContentValues> getValuesForNote(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        for (Note note : vCard.getNotes()) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Note.NOTE, note.getValue());

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addNote(VCard vCard, ContentValues noteValues) {
        String noteText = noteValues.getAsString(ContactsContract.CommonDataKinds.Note.NOTE);

        if (noteText != null) {
            Note note = new Note(noteText);
            vCard.addNote(note);
        }
    }

    protected static String[] getProjectionForPostalAddress() {
        return new String[] { ContactsContract.CommonDataKinds.StructuredPostal.TYPE, // 00
                ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, // 01
                ContactsContract.CommonDataKinds.StructuredPostal.LABEL, // 02
                ContactsContract.CommonDataKinds.StructuredPostal.STREET, // 03
                ContactsContract.CommonDataKinds.StructuredPostal.POBOX, // 04
                ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD, // 05
                ContactsContract.CommonDataKinds.StructuredPostal.CITY, // 06
                ContactsContract.CommonDataKinds.StructuredPostal.REGION, // 07
                ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, // 08
                ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY // 09
        };
    }

    protected static ContentValues getValuesForPostalAddress(Cursor cursor) {
        ContentValues values = new ContentValues(10);

        values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, cursor.getInt(0));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, cursor.getString(1));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, cursor.getString(2));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, cursor.getString(3));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.POBOX, cursor.getString(4));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD, cursor.getString(5));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, cursor.getString(6));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, cursor.getString(7));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, cursor.getString(8));
        values.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, cursor.getString(9));

        return values;
    }

    protected static List<ContentValues> getValuesForPostalAddresses(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        for (ezvcard.property.Address address : vCard.getAddresses()) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);

            if (address.getTypes().contains(AddressType.HOME))
                values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE,
                        ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME);
            else if (address.getTypes().contains(AddressType.WORK))
                values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE,
                        ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK);
            else if (!address.getTypes().isEmpty()) {
                values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE,
                        ContactsContract.CommonDataKinds.StructuredPostal.TYPE_CUSTOM);
                values.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL,
                        propertyNameToLabel(address.getTypes().iterator().next().getValue()));
            } else
                values.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE,
                        ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER);

            String formattedAddress = address.getLabel();
            if (StringUtils.isEmpty(address.getLabel())) {
                String lineStreet = StringUtils.join(new String[] { address.getStreetAddress(), address.getPoBox(),
                        address.getExtendedAddress() }, " ");

                String lineLocality = StringUtils
                        .join(new String[] { address.getPostalCode(), address.getLocality() }, " ");

                List<String> lines = new LinkedList<String>();
                if (lineStreet != null)
                    lines.add(lineStreet);
                if (address.getRegion() != null && !address.getRegion().isEmpty())
                    lines.add(address.getRegion());
                if (lineLocality != null)
                    lines.add(lineLocality);

                formattedAddress = StringUtils.join(lines, "\n");
            }
            values.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, formattedAddress);

            values.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, address.getStreetAddress());
            values.put(ContactsContract.CommonDataKinds.StructuredPostal.POBOX, address.getPoBox());
            values.put(ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD,
                    address.getExtendedAddress());
            values.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, address.getLocality());
            values.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, address.getRegion());
            values.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, address.getPostalCode());
            values.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, address.getCountry());

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addPostalAddress(String path, VCard vCard, ContentValues addressValues)
            throws InvalidComponentException {
        Integer addressType = addressValues.getAsInteger(ContactsContract.CommonDataKinds.StructuredPostal.TYPE);
        String formattedAddress = addressValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
        String label = addressValues.getAsString(ContactsContract.CommonDataKinds.StructuredPostal.LABEL);
        String street = addressValues.getAsString(ContactsContract.CommonDataKinds.StructuredPostal.STREET);
        String poBox = addressValues.getAsString(ContactsContract.CommonDataKinds.StructuredPostal.POBOX);
        String neighborhood = addressValues
                .getAsString(ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD);
        String city = addressValues.getAsString(ContactsContract.CommonDataKinds.StructuredPostal.CITY);
        String region = addressValues.getAsString(ContactsContract.CommonDataKinds.StructuredPostal.REGION);
        String postcode = addressValues.getAsString(ContactsContract.CommonDataKinds.StructuredPostal.POBOX);
        String country = addressValues.getAsString(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY);

        if (addressType != null && formattedAddress != null) {
            Address address = new Address();
            address.setLabel(formattedAddress);

            switch (addressType) {
            case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME:
                address.addType(AddressType.HOME);
                break;

            case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK:
                address.addType(AddressType.WORK);
                break;

            case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_CUSTOM:
                if (label != null)
                    AddressType.get(label);
                break;
            }

            if (street != null)
                address.setStreetAddress(street);

            if (poBox != null)
                address.setPoBox(poBox);

            if (neighborhood != null)
                address.setExtendedAddress(neighborhood);

            if (city != null)
                address.setLocality(city);

            if (region != null)
                address.setRegion(region);

            if (postcode != null)
                address.setPostalCode(postcode);

            if (country != null)
                address.setCountry(country);

            vCard.addAddress(address);
        } else {
            Log.e(TAG, "im address type or formatted address is null, not adding anything");
            throw new InvalidComponentException("im address type or formatted address is null", false,
                    CardDavConstants.CARDDAV_NAMESPACE, path);
        }
    }

    protected static String[] getProjectionForWebsite() {
        return new String[] { ContactsContract.CommonDataKinds.Website.URL // 00
        };
    }

    protected static ContentValues getValuesForWebsite(Cursor cursor) {
        ContentValues values = new ContentValues(1);

        values.put(ContactsContract.CommonDataKinds.Website.URL, cursor.getString(0));

        return values;
    }

    protected static List<ContentValues> getValuesForWebsites(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        for (Url url : vCard.getUrls()) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Website.URL, url.getValue());

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addWebsite(String path, VCard vCard, ContentValues websiteValues)
            throws InvalidComponentException {
        String urlText = websiteValues.getAsString(ContactsContract.CommonDataKinds.Website.URL);

        if (urlText != null) {
            Url url = new Url(urlText);
            vCard.addUrl(url);
        } else {
            Log.e(TAG, "url is null, not adding anything");
            throw new InvalidComponentException("url is null", false, CardDavConstants.CARDDAV_NAMESPACE, path);
        }
    }

    protected static String[] getProjectionForEvent() {
        return new String[] { ContactsContract.CommonDataKinds.Event.TYPE, // 00
                ContactsContract.CommonDataKinds.Event.LABEL, // 01
                ContactsContract.CommonDataKinds.Event.START_DATE // 02
        };
    }

    protected static ContentValues getValuesForEvent(Cursor cursor) {
        ContentValues values = new ContentValues(3);

        values.put(ContactsContract.CommonDataKinds.Event.TYPE, cursor.getInt(0));
        values.put(ContactsContract.CommonDataKinds.Event.LABEL, cursor.getString(1));
        values.put(ContactsContract.CommonDataKinds.Event.START_DATE, cursor.getString(2));

        return values;
    }

    protected static List<ContentValues> getValuesForEvents(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        if (vCard.getBirthday() != null && vCard.getBirthday().getDate() != null) {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Event.TYPE,
                    ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY);

            values.put(ContactsContract.CommonDataKinds.Event.START_DATE,
                    formatter.format(vCard.getBirthday().getDate()));

            valuesList.add(values);
        }

        if (vCard.getExtendedProperty(PROPERTY_EVENT_ANNIVERSARY) != null) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Event.TYPE,
                    ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY);
            values.put(ContactsContract.CommonDataKinds.Event.START_DATE,
                    vCard.getExtendedProperty(PROPERTY_EVENT_ANNIVERSARY).getValue());

            valuesList.add(values);
        }

        if (vCard.getExtendedProperty(PROPERTY_EVENT_OTHER) != null) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Event.TYPE,
                    ContactsContract.CommonDataKinds.Event.TYPE_OTHER);
            values.put(ContactsContract.CommonDataKinds.Event.START_DATE,
                    vCard.getExtendedProperty(PROPERTY_EVENT_OTHER).getValue());

            valuesList.add(values);
        }

        if (vCard.getExtendedProperty(PROPERTY_EVENT_CUSTOM) != null) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
            values.put(ContactsContract.CommonDataKinds.Event.TYPE,
                    ContactsContract.CommonDataKinds.Event.TYPE_CUSTOM);
            values.put(ContactsContract.CommonDataKinds.Event.LABEL,
                    vCard.getExtendedProperty(PROPERTY_EVENT_CUSTOM).getParameter(PARAMETER_EVENT_CUSTOM_LABEL));
            values.put(ContactsContract.CommonDataKinds.Event.START_DATE,
                    vCard.getExtendedProperty(PROPERTY_EVENT_CUSTOM).getValue());

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addEvent(String path, VCard vCard, ContentValues eventValues)
            throws InvalidComponentException {
        Integer eventType = eventValues.getAsInteger(ContactsContract.CommonDataKinds.Event.TYPE);
        String eventLabel = eventValues.getAsString(ContactsContract.CommonDataKinds.Event.LABEL);
        String eventStartDate = eventValues.getAsString(ContactsContract.CommonDataKinds.Event.START_DATE);
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);

        if (eventType != null && eventStartDate != null) {
            try {

                if (eventType == ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) {
                    Birthday birthday = new Birthday(formatter.parse(eventStartDate));
                    vCard.setBirthday(birthday);
                }

            } catch (ParseException e) {
                throw new InvalidComponentException("caught exception while parsing birthday", false,
                        CardDavConstants.CARDDAV_NAMESPACE, path);
            }

            if (eventType == ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY)
                vCard.setExtendedProperty(PROPERTY_EVENT_ANNIVERSARY, eventStartDate);
            else if (eventType == ContactsContract.CommonDataKinds.Event.TYPE_OTHER)
                vCard.setExtendedProperty(PROPERTY_EVENT_OTHER, eventStartDate);
            else if (eventType == ContactsContract.CommonDataKinds.Event.TYPE_CUSTOM)
                vCard.setExtendedProperty(PROPERTY_EVENT_CUSTOM, eventStartDate)
                        .setParameter(PARAMETER_EVENT_CUSTOM_LABEL, eventLabel);
        } else {
            Log.e(TAG, "event type or event start date is null, not adding anything");
            throw new InvalidComponentException("event type or event start date is null", false,
                    CardDavConstants.CARDDAV_NAMESPACE, path);
        }
    }

    protected static String[] getProjectionForSipAddress() {
        return new String[] { ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, // 00
                ContactsContract.CommonDataKinds.SipAddress.TYPE, // 01
                ContactsContract.CommonDataKinds.SipAddress.LABEL // 02
        };
    }

    protected static ContentValues getValuesForSipAddress(Cursor cursor) {
        ContentValues values = new ContentValues(3);

        values.put(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, cursor.getString(0));
        values.put(ContactsContract.CommonDataKinds.SipAddress.TYPE, cursor.getInt(1));
        values.put(ContactsContract.CommonDataKinds.SipAddress.LABEL, cursor.getString(2));

        return values;
    }

    protected static List<ContentValues> getValuesForSipAddresses(VCard vCard) {
        List<ContentValues> valuesList = new LinkedList<ContentValues>();

        for (RawProperty sipAddress : vCard.getExtendedProperties(PROPERTY_SIP)) {
            ContentValues values = new ContentValues();

            values.put(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE);

            values.put(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, sipAddress.getValue());

            values.put(ContactsContract.CommonDataKinds.SipAddress.TYPE,
                    ContactsContract.CommonDataKinds.SipAddress.TYPE_OTHER);

            valuesList.add(values);
        }

        return valuesList;
    }

    protected static void addSipAddress(VCard vCard, ContentValues sipAddressValues) {
        String sipAddress = sipAddressValues.getAsString(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);

        if (sipAddress != null)
            vCard.setExtendedProperty(PROPERTY_SIP, sipAddress);
    }

}