blob: 7ad31112d50690957d90ba6c83392b692fb7df23 [file] [log] [blame]
/*
* Copyright (C) 2008-2009 Marc Blank
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.exchange.adapter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import android.content.ContentProviderOperation;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Contacts;
import android.provider.Contacts.People;
import com.android.exchange.EasSyncService;
import com.android.exchange.EmailContent.Mailbox;
/**
* Sync adapter for EAS Contacts
*
*/
public class EasContactsSyncAdapter extends EasSyncAdapter {
private static final String WHERE_SERVER_ID_AND_ACCOUNT = "_sync_id=?";
ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
public EasContactsSyncAdapter(Mailbox mailbox) {
super(mailbox);
}
@Override
public boolean parse(ByteArrayInputStream is, EasSyncService service) throws IOException {
EasContactsSyncParser p = new EasContactsSyncParser(is, service);
return p.parse();
}
class EasContactsSyncParser extends EasContentParser {
String[] mBindArgument = new String[1];
String mMailboxIdAsString;
StringBuilder mExtraData = new StringBuilder(1024);
public EasContactsSyncParser(InputStream in, EasSyncService service) throws IOException {
super(in, service);
//setDebug(true); // DON'T CHECK IN WITH THIS UNCOMMENTED
}
class ContactMethod {
ContentValues values = new ContentValues();
ContactMethod(int kind, int type, String value) {
values.put(Contacts.ContactMethods.KIND, kind);
values.put(Contacts.ContactMethods.TYPE, type);
values.put(Contacts.ContactMethods.DATA, value);
}
}
class Phone {
ContentValues values = new ContentValues();
Phone(int type, String value) {
values.put(Contacts.Phones.TYPE, type);
values.put(Contacts.Phones.NUMBER, value);
}
}
@Override
public void wipe() {
// TODO Auto-generated method stub
}
void saveExtraData (int tag) throws IOException {
mExtraData.append(name);
mExtraData.append("~");
mExtraData.append(getValue());
mExtraData.append('~');
}
public void addData(String serverId, ArrayList<ContentProviderOperation> ops)
throws IOException {
String firstName = null;
String lastName = null;
String companyName = null;
ArrayList<ContactMethod> contactMethods = new ArrayList<ContactMethod>();
ArrayList<Phone> phones = new ArrayList<Phone>();
while (nextTag(EasTags.SYNC_APPLICATION_DATA) != END) {
switch (tag) {
case EasTags.CONTACTS_FIRST_NAME:
firstName = getValue();
break;
case EasTags.CONTACTS_LAST_NAME:
lastName = getValue();
break;
case EasTags.CONTACTS_COMPANY_NAME:
companyName = getValue();
break;
case EasTags.CONTACTS_EMAIL1_ADDRESS:
case EasTags.CONTACTS_EMAIL2_ADDRESS:
case EasTags.CONTACTS_EMAIL3_ADDRESS:
contactMethods.add(new ContactMethod(Contacts.KIND_EMAIL,
Contacts.ContactMethods.TYPE_OTHER, getValue()));
break;
case EasTags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER:
case EasTags.CONTACTS_BUSINESS_TELEPHONE_NUMBER:
phones.add(new Phone(Contacts.Phones.TYPE_WORK, getValue()));
break;
case EasTags.CONTACTS_BUSINESS_FAX_NUMBER:
phones.add(new Phone(Contacts.Phones.TYPE_FAX_WORK, getValue()));
break;
case EasTags.CONTACTS_HOME_FAX_NUMBER:
phones.add(new Phone(Contacts.Phones.TYPE_FAX_HOME, getValue()));
break;
case EasTags.CONTACTS_HOME_TELEPHONE_NUMBER:
case EasTags.CONTACTS_HOME2_TELEPHONE_NUMBER:
phones.add(new Phone(Contacts.Phones.TYPE_HOME, getValue()));
break;
case EasTags.CONTACTS_MOBILE_TELEPHONE_NUMBER:
case EasTags.CONTACTS_CAR_TELEPHONE_NUMBER:
phones.add(new Phone(Contacts.Phones.TYPE_MOBILE, getValue()));
break;
case EasTags.CONTACTS_PAGER_NUMBER:
phones.add(new Phone(Contacts.Phones.TYPE_PAGER, getValue()));
break;
// All tags that we don't use (except for a few like picture and body) need to
// be saved, even if we're not using them. Otherwise, when we upload changes,
// those items will be deleted back on the server.
case EasTags.CONTACTS_ANNIVERSARY:
case EasTags.CONTACTS_ASSISTANT_NAME:
case EasTags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER:
case EasTags.CONTACTS_BIRTHDAY:
case EasTags.CONTACTS_BUSINESS_ADDRESS_CITY:
case EasTags.CONTACTS_BUSINESS_ADDRESS_COUNTRY:
case EasTags.CONTACTS_BUSINESS_ADDRESS_POSTAL_CODE:
case EasTags.CONTACTS_BUSINESS_ADDRESS_STATE:
case EasTags.CONTACTS_BUSINESS_ADDRESS_STREET:
case EasTags.CONTACTS_CATEGORIES:
case EasTags.CONTACTS_CATEGORY:
case EasTags.CONTACTS_CHILDREN:
case EasTags.CONTACTS_CHILD:
case EasTags.CONTACTS_DEPARTMENT:
case EasTags.CONTACTS_FILE_AS:
case EasTags.CONTACTS_HOME_ADDRESS_CITY:
case EasTags.CONTACTS_HOME_ADDRESS_COUNTRY:
case EasTags.CONTACTS_HOME_ADDRESS_POSTAL_CODE:
case EasTags.CONTACTS_HOME_ADDRESS_STATE:
case EasTags.CONTACTS_HOME_ADDRESS_STREET:
case EasTags.CONTACTS_JOB_TITLE:
case EasTags.CONTACTS_MIDDLE_NAME:
case EasTags.CONTACTS_OFFICE_LOCATION:
case EasTags.CONTACTS_OTHER_ADDRESS_CITY:
case EasTags.CONTACTS_OTHER_ADDRESS_COUNTRY:
case EasTags.CONTACTS_OTHER_ADDRESS_POSTAL_CODE:
case EasTags.CONTACTS_OTHER_ADDRESS_STATE:
case EasTags.CONTACTS_OTHER_ADDRESS_STREET:
case EasTags.CONTACTS_RADIO_TELEPHONE_NUMBER:
case EasTags.CONTACTS_SPOUSE:
case EasTags.CONTACTS_SUFFIX:
case EasTags.CONTACTS_TITLE:
case EasTags.CONTACTS_WEBPAGE:
case EasTags.CONTACTS_YOMI_COMPANY_NAME:
case EasTags.CONTACTS_YOMI_FIRST_NAME:
case EasTags.CONTACTS_YOMI_LAST_NAME:
case EasTags.CONTACTS_COMPRESSED_RTF:
//case EasTags.CONTACTS_PICTURE:
case EasTags.CONTACTS2_CUSTOMER_ID:
case EasTags.CONTACTS2_GOVERNMENT_ID:
case EasTags.CONTACTS2_IM_ADDRESS:
case EasTags.CONTACTS2_IM_ADDRESS_2:
case EasTags.CONTACTS2_IM_ADDRESS_3:
case EasTags.CONTACTS2_MANAGER_NAME:
case EasTags.CONTACTS2_COMPANY_MAIN_PHONE:
case EasTags.CONTACTS2_ACCOUNT_NAME:
case EasTags.CONTACTS2_NICKNAME:
case EasTags.CONTACTS2_MMS:
saveExtraData(tag);
break;
default:
skipTag();
}
}
// Ok, ready to create our contact...
// First pass, no batch... Eventually, move to changesParser
ContentValues values = new ContentValues();
// TODO Do something with the extras (i.e. find a home for them)
String extraData = mExtraData.toString();
mService.userLog(extraData);
// We must have first name, last name, or company name
String name;
if (firstName != null || lastName != null) {
if (firstName == null) {
name = lastName;
} else if (lastName == null) {
name = firstName;
} else {
name = firstName + ' ' + lastName;
}
} else if (companyName != null) {
name = companyName;
} else {
return;
}
values.put(Contacts.People.NAME, name);
values.put("_sync_id", serverId);
// TODO Use proper value here; need to ask jham
//values.put("_sync_account", "EAS");
Uri contactUri =
Contacts.People.createPersonInMyContactsGroup(mContentResolver, values);
Uri contactMethodsUri = Uri.withAppendedPath(contactUri,
Contacts.People.ContactMethods.CONTENT_DIRECTORY);
for (ContactMethod cm: contactMethods) {
mContentResolver.insert(contactMethodsUri, cm.values);
//ops.add(ContentProviderOperation
// .newInsert(contactMethodsUri).withValues(cm.values).build());
}
Uri phoneUri = Uri.withAppendedPath(contactUri, People.Phones.CONTENT_DIRECTORY);
for (Phone phone: phones) {
mContentResolver.insert(phoneUri, phone.values);
//ops.add(ContentProviderOperation
// .newInsert(phoneUri).withValues(phone.values).build());
}
}
public void addParser(ArrayList<ContentProviderOperation> ops) throws IOException {
String serverId = null;
while (nextTag(EasTags.SYNC_ADD) != END) {
switch (tag) {
case EasTags.SYNC_SERVER_ID: // same as
serverId = getValue();
break;
case EasTags.SYNC_APPLICATION_DATA:
addData(serverId, ops);
break;
default:
skipTag();
}
}
}
private Cursor getServerIdCursor(String serverId) {
mBindArgument[0] = serverId;
//bindArguments[1] = "EAS";
// TODO Find proper constant for _id
return mContentResolver.query(Contacts.People.CONTENT_URI, new String[] {"_id"},
WHERE_SERVER_ID_AND_ACCOUNT, mBindArgument, null);
}
public void deleteParser(ArrayList<ContentProviderOperation> ops) throws IOException {
while (nextTag(EasTags.SYNC_DELETE) != END) {
switch (tag) {
case EasTags.SYNC_SERVER_ID:
String serverId = getValue();
// Find the message in this mailbox with the given serverId
Cursor c = getServerIdCursor(serverId);
try {
if (c.moveToFirst()) {
mService.userLog("Deleting " + serverId);
mContentResolver.delete(ContentUris
.withAppendedId(Contacts.People.CONTENT_URI, c.getLong(0)),
null, null);
//ops.add(ContentProviderOperation.newDelete(
// ContentUris.withAppendedId(Contacts.People.CONTENT_URI,
// c.getLong(0))).build());
}
} finally {
c.close();
}
break;
default:
skipTag();
}
}
}
class ServerChange {
long id;
boolean read;
ServerChange(long _id, boolean _read) {
id = _id;
read = _read;
}
}
/**
* A change operation on a contact is implemented as a delete followed by an add, since the
* change data is always a full contact.
*
* @param ops the array of pending ContactProviderOperations.
* @throws IOException
*/
public void changeParser(ArrayList<ContentProviderOperation> ops) throws IOException {
String serverId = null;
while (nextTag(EasTags.SYNC_CHANGE) != END) {
switch (tag) {
case EasTags.SYNC_SERVER_ID:
serverId = getValue();
Cursor c = getServerIdCursor(serverId);
try {
if (c.moveToFirst()) {
mContentResolver.delete(ContentUris
.withAppendedId(Contacts.People.CONTENT_URI, c.getLong(0)),
null, null);
//ops.add(ContentProviderOperation.newDelete(
// ContentUris.withAppendedId(Contacts.People.CONTENT_URI,
// c.getLong(0))).build());
mService.userLog("Changing " + serverId);
}
} finally {
c.close();
}
break;
case EasTags.SYNC_APPLICATION_DATA:
addData(serverId, ops);
default:
skipTag();
}
}
}
public void commandsParser() throws IOException {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
while (nextTag(EasTags.SYNC_COMMANDS) != END) {
if (tag == EasTags.SYNC_ADD) {
addParser(ops);
} else if (tag == EasTags.SYNC_DELETE) {
deleteParser(ops);
} else if (tag == EasTags.SYNC_CHANGE) {
changeParser(ops);
} else
skipTag();
}
// Batch provider operations here
// try {
// mService.mContext.getContentResolver()
// .applyBatch(ContactsProvider.EMAIL_AUTHORITY, ops);
// } catch (RemoteException e) {
// // There is nothing to be done here; fail by returning null
// } catch (OperationApplicationException e) {
// // There is nothing to be done here; fail by returning null
// }
mService.userLog("Contacts SyncKey confirmed as: " + mMailbox.mSyncKey);
}
}
@Override
public void cleanup(EasSyncService service) {
}
@Override
public String getCollectionName() {
return "Contacts";
}
@Override
public boolean sendLocalChanges(EasSerializer s, EasSyncService service) throws IOException {
return false;
}
}