/* * Copyright 2010 Arthur Zaczek <arthur@dasz.at>, dasz.at OG; All rights reserved. * Copyright 2010 David Schmitt <david@dasz.at>, dasz.at OG; All rights reserved. * * This file is part of Kolab Sync for Android. * Kolab Sync for Android 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. * Kolab Sync for Android 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 Kolab Sync for Android. * If not, see <http://www.gnu.org/licenses/>. */ package at.dasz.KolabDroid.ContactsContract; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Flags.Flag; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import android.util.Log; import at.dasz.KolabDroid.Utils; import at.dasz.KolabDroid.Provider.LocalCacheProvider; import at.dasz.KolabDroid.Settings.Settings; import at.dasz.KolabDroid.Sync.AbstractSyncHandler; import at.dasz.KolabDroid.Sync.CacheEntry; import at.dasz.KolabDroid.Sync.SyncContext; import at.dasz.KolabDroid.Sync.SyncException; public class SyncContactsHandler extends AbstractSyncHandler { //private static final String[] PEOPLE_ID_PROJECTION = new String[] { People._ID }; /* private static final String[] PHONE_PROJECTION = new String[] { Contacts.Phones.TYPE, Contacts.Phones.NUMBER }; private static final String[] EMAIL_PROJECTION = new String[] { Contacts.ContactMethods.TYPE, Contacts.ContactMethods.DATA }; //private static final String[] PEOPLE_NAME_PROJECTION = new String[] { People.NAME }; private static final String[] CONTACT_NAME_PROJECTION = new String[] { ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME }; private static final String[] ID_PROJECTION = new String[] { "_id" }; private static final String EMAIL_FILTER = Contacts.ContactMethods.KIND + "=" + Contacts.KIND_EMAIL; */ private final String defaultFolderName; private final LocalCacheProvider cacheProvider; private final ContentResolver cr; public SyncContactsHandler(Context context) { super(context); Settings s = new Settings(context); settings = s; defaultFolderName = s.getContactsFolder(); cacheProvider = new LocalCacheProvider.ContactsCacheProvider(context); cr = context.getContentResolver(); status.setTask("Contacts"); } public String getDefaultFolderName() { return defaultFolderName; } public boolean shouldProcess() { boolean hasFolder = (defaultFolderName != null && !"".equals(defaultFolderName)); return settings.getSyncContacts() && hasFolder; } public LocalCacheProvider getLocalCacheProvider() { return cacheProvider; } public Cursor getAllLocalItemsCursor() { //return only those which are not deleted by other programs //String where = ContactsContract.RawContacts.DELETED+"='0'"; //return all again return cr.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{ContactsContract.RawContacts._ID}, null, null, null); } public int getIdColumnIndex(Cursor c) { return c.getColumnIndex(ContactsContract.RawContacts._ID); } @Override public void createLocalItemFromServer(Session session, Folder targetFolder, SyncContext sync) throws MessagingException, ParserConfigurationException, IOException, SyncException { Log.d("sync", "Downloading item ..."); try { InputStream xmlinput = extractXml(sync.getMessage()); Document doc = Utils.getDocument(xmlinput); updateLocalItemFromServer(sync, doc); updateCacheEntryFromMessage(sync); if(this.settings.getMergeContactsByName()) { Log.d("ConH", "Preparing upload of Contact after merge"); sync.setLocalItem(null); getLocalItem(sync); //fetch updates which were just done Log.d("ConH", "Fetched data after merge for " + ((Contact)sync.getLocalItem()).getFullName()); updateServerItemFromLocal(sync, doc); Log.d("ConH", "Server item updated after merge"); // Create & Upload new Message // IMAP needs a new Message uploaded String xml = Utils.getXml(doc); Message newMessage = wrapXmlInMessage(session, sync, xml); targetFolder.appendMessages(new Message[] { newMessage }); newMessage.saveChanges(); // Delete old message sync.getMessage().setFlag(Flag.DELETED, true); // Replace sync context with new message sync.setMessage(newMessage); Log.d("ConH", "IMAP Message replaced after merge"); updateCacheEntryFromMessage(sync); } } catch (SAXException ex) { throw new SyncException(getItemText(sync), "Unable to extract XML Document", ex); } } @Override protected void updateLocalItemFromServer(SyncContext sync, Document xml) throws SyncException { Contact contact = (Contact) sync.getLocalItem(); if (contact == null) { contact = new Contact(); } Element root = xml.getDocumentElement(); contact.setUid(Utils.getXmlElementString(root, "uid")); Element name = Utils.getXmlElement(root, "name"); if (name != null) { //contact.setFullName(Utils.getXmlElementString(name, "full-name")); String fullName = Utils.getXmlElementString(name, "full-name"); if(fullName != null) { String[] names = fullName.split(" "); if(names.length == 2) { contact.setGivenName(names[0]); contact.setFamilyName(names[1]); } } } contact.setBirthday(Utils.getXmlElementString(root, "birthday")); contact.getContactMethods().clear(); NodeList nl = Utils.getXmlElements(root, "phone"); for (int i = 0; i < nl.getLength(); i++) { ContactMethod cm = new PhoneContact(); cm.fromXml((Element) nl.item(i)); contact.getContactMethods().add(cm); } nl = Utils.getXmlElements(root, "email"); for (int i = 0; i < nl.getLength(); i++) { ContactMethod cm = new EmailContact(); cm.fromXml((Element) nl.item(i)); contact.getContactMethods().add(cm); } sync.setCacheEntry(saveContact(contact)); } @Override protected void updateServerItemFromLocal(SyncContext sync, Document xml) throws SyncException, MessagingException { Contact source = getLocalItem(sync); CacheEntry entry = sync.getCacheEntry(); entry.setLocalHash(source.getLocalHash()); final Date lastChanged = new Date(); entry.setRemoteChangedDate(lastChanged); writeXml(xml, source, lastChanged); } private void writeXml(Document xml, Contact source, final Date lastChanged) { Element root = xml.getDocumentElement(); //TODO: needs to be above contact information (Kmail bug?) //Kmail seems to be picky about <phone> and <email> elements they should be right after each other //remove it for now Utils.deleteXmlElements(root, "last-modification-date"); //we do not need this one for now //if we need it, put below contact methods (otherwise kmail complains)... //TODO: what shall we do with this entry? :) Utils.deleteXmlElements(root, "preferred-address"); /* Utils.setXmlElementValue(xml, root, "last-modification-date", Utils .toUtc(lastChanged)); */ Utils.setXmlElementValue(xml, root, "uid", source.getUid()); Element name = Utils.getOrCreateXmlElement(xml, root, "name"); Utils.setXmlElementValue(xml, name, "full-name", source.getFullName()); Utils.setXmlElementValue(xml, name, "given-name", source.getGivenName()); Utils.setXmlElementValue(xml, name, "last-name", source.getFamilyName()); Utils.setXmlElementValue(xml, root, "birthday", source.getBirthday()); Utils.deleteXmlElements(root, "phone"); Utils.deleteXmlElements(root, "email"); for (ContactMethod cm : source.getContactMethods()) { cm.toXml(xml, root, source.getFullName()); } } @Override protected String writeXml(SyncContext sync) throws ParserConfigurationException, SyncException, MessagingException { Contact source = getLocalItem(sync); CacheEntry entry = sync.getCacheEntry(); entry.setLocalHash(source.getLocalHash()); final Date lastChanged = new Date(); entry.setRemoteChangedDate(lastChanged); final String newUid = getNewUid(); entry.setRemoteId(newUid); source.setUid(newUid); Document xml = Utils.newDocument("contact"); writeXml(xml, source, lastChanged); return Utils.getXml(xml); } @Override protected String getMimeType() { return "application/x-vnd.kolab.contact"; } public boolean hasLocalItem(SyncContext sync) throws SyncException, MessagingException { return getLocalItem(sync) != null; } public boolean hasLocalChanges(SyncContext sync) throws SyncException, MessagingException { CacheEntry e = sync.getCacheEntry(); Contact contact = getLocalItem(sync);; String entryHash = e.getLocalHash(); String contactHash = contact != null ? contact.getLocalHash() : ""; return !entryHash.equals(contactHash); } @Override public void deleteLocalItem(int localId) { ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); //normal delete first, then with syncadapter flag Uri rawUri = ContactsContract.RawContacts.CONTENT_URI; ops.add(ContentProviderOperation.newDelete(rawUri). withSelection(ContactsContract.RawContacts._ID + "=?", new String[]{String.valueOf(localId)}). build()); //remove contact from raw_contact table (this time with syncadapter flag set) rawUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); ops.add(ContentProviderOperation.newDelete(rawUri). withSelection(ContactsContract.RawContacts._ID + "=?", new String[]{String.valueOf(localId)}). build()); try { cr.applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { Log.e("EE", e.toString()); } } private void deleteLocalItemFinally(int localId) { ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); //remove contact from raw_contact table (with syncadapter flag set) Uri rawUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(); ops.add(ContentProviderOperation.newDelete(rawUri). withSelection(ContactsContract.RawContacts._ID + "=?", new String[]{String.valueOf(localId)}). build()); try { cr.applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { Log.e("EE", e.toString()); } } @Override public void deleteServerItem(SyncContext sync) throws MessagingException, SyncException { Log.d("sync", "Deleting from server: " + sync.getMessage().getSubject()); sync.getMessage().setFlag(Flag.DELETED, true); // remove contents too, to avoid confusing the butchered JAF // message.setContent("", "text/plain"); // message.saveChanges(); getLocalCacheProvider().deleteEntry(sync.getCacheEntry()); //make sure it gets flushed from the raw_contacts table on the phone as well deleteLocalItemFinally(sync.getCacheEntry().getLocalId()); } private CacheEntry saveContact(Contact contact) throws SyncException { Uri uri = null; String name = contact.getFullName(); String firstName = contact.getGivenName(); String lastName = contact.getFamilyName(); String email = ""; String phone = ""; Log.d("ConH", "Saving Contact: \"" + name + "\""); ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); boolean doMerge = false; if (contact.getId() == 0 && this.settings.getMergeContactsByName()) { //find raw_contact by name String w = ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME +"='"+name+"'"; //Cursor c = cr.query(ContactsContract.RawContacts.CONTENT_URI, null, w, null, null); Cursor c = cr.query(ContactsContract.Data.CONTENT_URI, null, w, null, null); if(c == null) { Log.d("ConH", "SC: faild to query for merge with contact: " + name); } if(c.getCount()>0) { c.moveToFirst(); //int nameIdx = c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); //String c.getString(nameIdx); //int rawIdIdx = c.getColumnIndex(ContactsContract.RawContacts._ID); int rawIdIdx = c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID); int rawID = c.getInt(rawIdIdx); contact.setId(rawID); doMerge = true; Log.d("ConH", "SC: Found Entry ID: " + rawID + " for contact: " + name + " -> will merge now"); } if(c != null) c.close(); } if (contact.getId() == 0) { Log.d("ConH", "SC: Contact " + name + " is NEW -> insert"); String accountName = settings.getAccountName(); if("".equals(accountName)) accountName = null; String accountType = settings.getAccountType(); if("".equals(accountType)) accountType = null; ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) .build()); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, firstName) .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName) .build()); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, contact.getBirthday()) .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) .build()); for (ContactMethod cm : contact.getContactMethods()) { if(cm instanceof EmailContact) { email = cm.getData(); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Email.DATA, email). withValue(ContactsContract.CommonDataKinds.Email.TYPE, cm.getType()).build()); } if(cm instanceof PhoneContact) { phone = cm.getData(); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, cm.getType()) .build()); } } } else { Log.d("ConH", "SC. Contact " +name+" already in Android book, MergeFlag: " + doMerge); Uri updateUri = ContactsContract.Data.CONTENT_URI; List<ContactMethod> cms = null; List<ContactMethod> mergedCms = new ArrayList<ContactMethod>(); //first remove stuff that is in addressbook Cursor phoneCursor = null, emailCursor = null, birthdayCursor = null; //update name (broken at the moment :() /* ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) .withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.getId()) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, firstName) .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName) .build()); */ //birthday { String w = ContactsContract.Data.RAW_CONTACT_ID+"='"+contact.getId()+"' AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '"+ ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE+"'"; //Log.i("II", "w: " + w); birthdayCursor = cr.query(updateUri, null, w, null, null); if (birthdayCursor == null) throw new SyncException( "EE", "cr.query returned null"); if(birthdayCursor.getCount() > 0) // otherwise no events { if (!birthdayCursor.moveToFirst()) return null; int idCol = birthdayCursor.getColumnIndex(ContactsContract.Data._ID); //int dateCol = birthdayCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE); int typeCol = birthdayCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE); if(birthdayCursor.getInt(typeCol) == ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) { ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI). withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(birthdayCursor.getInt(idCol))}). build()); } } if(! "".equals(contact.getBirthday())) { ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.getId()) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, contact.getBirthday()) .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) .build()); Log.d("ConH", "Writing birthday: " + contact.getBirthday() + " for contact " + name); } } //phone { String w = ContactsContract.Data.RAW_CONTACT_ID+"='"+contact.getId()+"' AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '"+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE+"'"; //Log.i("II", "w: " + w); phoneCursor = cr.query(updateUri, null, w, null, null); if (phoneCursor == null) throw new SyncException( "EE", "cr.query returned null"); if(phoneCursor.getCount() > 0) // otherwise no phone numbers { if (!phoneCursor.moveToFirst()) return null; int idCol = phoneCursor.getColumnIndex(ContactsContract.Data._ID); int numberCol = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); int typeCol = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE); if(!doMerge) { do { ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI). withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(phoneCursor.getInt(idCol))}). build()); }while (phoneCursor.moveToNext()); } else { for(ContactMethod cm : contact.getContactMethods()) { if(! (cm instanceof PhoneContact)) continue; boolean found = false; String newNumber = cm.getData(); int newType = cm.getType(); do { String numberIn = phoneCursor.getString(numberCol); int typeIn = phoneCursor.getInt(typeCol); if(typeIn == newType && numberIn.equals(newNumber)) { Log.d("ConH", "SC: Found phone: " + numberIn + " for contact " + name + " -> wont add"); found = true; break; } }while(phoneCursor.moveToNext()); if(!found) { mergedCms.add(cm); } } } } else { if(doMerge) { Log.d("ConH", "SC: No numbers in android for contact " + name + " -> adding all"); //we can add all new Numbers for(ContactMethod cm : contact.getContactMethods()) { if(! (cm instanceof PhoneContact)) continue; mergedCms.add(cm); } } } } //mail { String w = ContactsContract.Data.RAW_CONTACT_ID+"='"+contact.getId()+"' AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '"+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE+"'"; emailCursor = cr.query(updateUri, null, w, null, null); if (emailCursor == null) throw new SyncException( "EE", "cr.query returned null"); if(emailCursor.getCount() > 0) // otherwise no email addresses { if (!emailCursor.moveToFirst()) return null; int idCol = emailCursor.getColumnIndex(ContactsContract.Data._ID); int mailCol = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA); int typeCol = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE); if(!doMerge) { do { ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI). withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(emailCursor.getInt(idCol))}). build()); }while (emailCursor.moveToNext()); } else { for(ContactMethod cm : contact.getContactMethods()) { if(! (cm instanceof EmailContact)) continue; boolean found = false; String newMail = cm.getData(); int newType = cm.getType(); do { String emailIn = emailCursor.getString(mailCol); int typeIn = emailCursor.getInt(typeCol); if(typeIn == newType && emailIn.equals(newMail)) { Log.d("ConH", "SC. Found email: " + emailIn + " for contact " + name + " -> wont add"); found = true; break; } }while(emailCursor.moveToNext()); if(!found) { mergedCms.add(cm); } } } } else { if(doMerge) { Log.d("ConH", "SC: No email in android for contact " + name + " -> adding all"); //we can add all new Numbers for(ContactMethod cm : contact.getContactMethods()) { if(! (cm instanceof EmailContact)) continue; mergedCms.add(cm); } } } } //insert again if(doMerge) { cms = mergedCms; } else { cms = contact.getContactMethods(); } //for (ContactMethod cm : contact.getContactMethods()) for (ContactMethod cm : cms) { if(cm instanceof EmailContact) { email = cm.getData(); Log.d("ConH", "SC: Writing mail: " + email + " for contact " + name); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.getId()) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Email.DATA, email). withValue(ContactsContract.CommonDataKinds.Email.TYPE, cm.getType()).build()); } if(cm instanceof PhoneContact) { phone = cm.getData(); Log.d("ConH", "Writing phone: " + phone + " for contact " + name); ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.getId()) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, cm.getType()) .build()); } } if (phoneCursor != null) phoneCursor.close(); if (emailCursor != null) emailCursor.close(); } //Log.i("II", "Creating contact: " + firstName + " " + lastName); try { ContentProviderResult[] results = cr.applyBatch(ContactsContract.AUTHORITY, ops); //store the first result: it contains the uri of the raw contact with its ID if(contact.getId() == 0) { uri = results[0].uri; } else { uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, contact.getId()); } Log.d("ConH", "SC: Affected Uri was: " + uri); } catch (Exception e) { // Log exception Log.e("EE","Exception encountered while inserting contact: " + e.getMessage() + e.getStackTrace()); } CacheEntry result = new CacheEntry(); result.setLocalId((int) ContentUris.parseId(uri)); result.setLocalHash(contact.getLocalHash()); result.setRemoteId(contact.getUid()); return result; } private Contact getLocalItem(SyncContext sync) throws SyncException, MessagingException { if (sync.getLocalItem() != null) return (Contact) sync.getLocalItem(); Uri uri = ContactsContract.Data.CONTENT_URI; Cursor personCursor = null, phoneCursor = null, emailCursor = null, birthdayCursor = null; try { String where = ContactsContract.Data.RAW_CONTACT_ID+"='"+sync.getCacheEntry().getLocalId()+"' AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '"+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE+"'"; //Log.i("II", "where: " + where); personCursor = cr.query(uri, null, where, null, null); if (personCursor == null) throw new SyncException( getItemText(sync), "cr.query returned null"); if (!personCursor.moveToFirst()) return null; //int idx = personCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); int idxFirst = personCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME); int idxLast = personCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME); String firstName = personCursor.getString(idxFirst); String lastName = personCursor.getString(idxLast); //String name = firstName + " " + lastName; //Log.i("II", "Cursor Line" +name); Contact result = new Contact(); result.setId(sync.getCacheEntry().getLocalId()); result.setUid(sync.getCacheEntry().getRemoteId()); //result.setFullName(personCursor.getString(nameIdx)); result.setGivenName(firstName); result.setFamilyName(lastName); //phone { String w = ContactsContract.Data.RAW_CONTACT_ID+"='"+sync.getCacheEntry().getLocalId()+"' AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '"+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE+"'"; phoneCursor = cr.query(uri, null, w, null, null); if (phoneCursor == null) throw new SyncException( getItemText(sync), "cr.query returned null"); if(phoneCursor.getCount() > 0) // otherwise no phone numbers { if (!phoneCursor.moveToFirst()) return null; int numberIdx = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); int typeIdx = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE); do { PhoneContact pc = new PhoneContact(); pc.setData(phoneCursor.getString(numberIdx)); pc.setType(phoneCursor.getInt(typeIdx)); result.getContactMethods().add(pc); }while (phoneCursor.moveToNext()); } } //mail { String w = ContactsContract.Data.RAW_CONTACT_ID+"='"+sync.getCacheEntry().getLocalId()+"' AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '"+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE+"'"; emailCursor = cr.query(uri, null, w, null, null); if (emailCursor == null) throw new SyncException( getItemText(sync), "cr.query returned null"); if(emailCursor.getCount() > 0) // otherwise no email addresses { if (!emailCursor.moveToFirst()) return null; int dataIdx = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA); //int typeIdx = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE); do { EmailContact pc = new EmailContact(); pc.setData(emailCursor.getString(dataIdx)); //pc.setType(emailCursor.getInt(typeIdx)); result.getContactMethods().add(pc); }while (emailCursor.moveToNext()); } } //birthday { String w = ContactsContract.Data.RAW_CONTACT_ID+"='"+sync.getCacheEntry().getLocalId()+"' AND " + ContactsContract.Contacts.Data.MIMETYPE + " = '"+ ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE +"'"; birthdayCursor = cr.query(uri, null, w, null, null); if (birthdayCursor == null) throw new SyncException( getItemText(sync), "cr.query returned null"); if(birthdayCursor.getCount() > 0) // otherwise no birthday { if (!birthdayCursor.moveToFirst()) return null; int dateIdx = birthdayCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE); int typeIdx = birthdayCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE); //int typeIdx = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE); //do //{ int typeIn = birthdayCursor.getInt(typeIdx); if(typeIn == ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) { String bday = birthdayCursor.getString(dateIdx); result.setBirthday(bday); } //}while (birthdayCursor.moveToNext()); } } sync.setLocalItem(result); return result; } finally { if (personCursor != null) personCursor.close(); if (phoneCursor != null) phoneCursor.close(); if (emailCursor != null) emailCursor.close(); } } private String getNewUid() { // Create Application and Type specific id // kd == Kolab Droid, ct = contact return "kd-ct-" + UUID.randomUUID().toString(); } @Override protected String getMessageBodyText(SyncContext sync) throws SyncException, MessagingException { Contact contact = getLocalItem(sync); StringBuilder sb = new StringBuilder(); String fullName =contact.getFullName(); sb.append(fullName == null ? "(no name)" : fullName); sb.append("\n"); sb.append("----- Contact Methods -----\n"); for (ContactMethod cm : contact.getContactMethods()) { sb.append(cm.getData()); sb.append("\n"); } return sb.toString(); } @Override public String getItemText(SyncContext sync) throws MessagingException { if (sync.getLocalItem() != null) { Contact item = (Contact) sync.getLocalItem(); return item.getFullName(); } else { return sync.getMessage().getSubject(); } } }