386 lines
16 KiB
Java
386 lines
16 KiB
Java
|
/*
|
||
|
* 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("SyncKey confirmed as: " + mMailbox.mSyncKey);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String getCollectionName() {
|
||
|
return "Contacts";
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean sendLocalChanges(EasSerializer s, EasSyncService service) throws IOException {
|
||
|
return false;
|
||
|
}
|
||
|
}
|