2009-07-05 19:54:49 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2009-07-23 22:31:28 +00:00
|
|
|
import com.android.email.provider.EmailContent.Mailbox;
|
2009-07-27 17:24:58 +00:00
|
|
|
import com.android.email.provider.EmailContent.MailboxColumns;
|
|
|
|
import com.android.exchange.Eas;
|
2009-07-23 22:31:28 +00:00
|
|
|
import com.android.exchange.EasSyncService;
|
2009-07-05 19:54:49 +00:00
|
|
|
|
|
|
|
import android.content.ContentProviderOperation;
|
2009-07-27 17:24:58 +00:00
|
|
|
import android.content.ContentProviderResult;
|
|
|
|
import android.content.ContentResolver;
|
2009-07-05 19:54:49 +00:00
|
|
|
import android.content.ContentUris;
|
|
|
|
import android.content.ContentValues;
|
2009-07-27 17:24:58 +00:00
|
|
|
import android.content.Entity;
|
|
|
|
import android.content.EntityIterator;
|
|
|
|
import android.content.OperationApplicationException;
|
|
|
|
import android.content.ContentProviderOperation.Builder;
|
|
|
|
import android.content.Entity.NamedContentValues;
|
2009-07-05 19:54:49 +00:00
|
|
|
import android.database.Cursor;
|
|
|
|
import android.net.Uri;
|
2009-07-27 17:24:58 +00:00
|
|
|
import android.os.RemoteException;
|
|
|
|
import android.provider.ContactsContract;
|
|
|
|
import android.provider.ContactsContract.Data;
|
|
|
|
import android.provider.ContactsContract.RawContacts;
|
|
|
|
import android.provider.ContactsContract.CommonDataKinds.Email;
|
|
|
|
import android.provider.ContactsContract.CommonDataKinds.Im;
|
|
|
|
import android.provider.ContactsContract.CommonDataKinds.Note;
|
|
|
|
import android.provider.ContactsContract.CommonDataKinds.Organization;
|
|
|
|
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
|
|
|
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
|
|
|
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
|
|
|
import android.util.Log;
|
2009-07-05 19:54:49 +00:00
|
|
|
|
2009-07-23 22:31:28 +00:00
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.util.ArrayList;
|
2009-07-05 19:54:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sync adapter for EAS Contacts
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public class EasContactsSyncAdapter extends EasSyncAdapter {
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
private static final String TAG = "EasContactsSyncAdapter";
|
|
|
|
private static final String SERVER_ID_SELECTION = RawContacts.SOURCE_ID + "=?";
|
|
|
|
private static final String[] ID_PROJECTION = new String[] {RawContacts._ID};
|
2009-07-05 19:54:49 +00:00
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
// Note: These constants are likely to change; they are internal to this class now, but
|
|
|
|
// may end up in the provider.
|
|
|
|
private static final int TYPE_EMAIL1 = 20;
|
|
|
|
private static final int TYPE_EMAIL2 = 21;
|
|
|
|
private static final int TYPE_EMAIL3 = 22;
|
|
|
|
|
|
|
|
private static final int TYPE_IM1 = 23;
|
|
|
|
private static final int TYPE_IM2 = 24;
|
|
|
|
private static final int TYPE_IM3 = 25;
|
|
|
|
|
|
|
|
private static final int TYPE_WORK2 = 26;
|
|
|
|
private static final int TYPE_HOME2 = 27;
|
|
|
|
private static final int TYPE_CAR = 28;
|
|
|
|
private static final int TYPE_COMPANY_MAIN = 29;
|
|
|
|
private static final int TYPE_MMS = 30;
|
|
|
|
private static final int TYPE_RADIO = 31;
|
2009-07-05 19:54:49 +00:00
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
|
2009-07-05 19:54:49 +00:00
|
|
|
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
public EasContactsSyncAdapter(Mailbox mailbox, EasSyncService service) {
|
|
|
|
super(mailbox, service);
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean parse(ByteArrayInputStream is, EasSyncService service) throws IOException {
|
|
|
|
EasContactsSyncParser p = new EasContactsSyncParser(is, service);
|
|
|
|
return p.parse();
|
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
public static final class Extras {
|
|
|
|
private Extras() {}
|
|
|
|
|
|
|
|
/** MIME type used when storing this in data table. */
|
|
|
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/easextras";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The note text.
|
|
|
|
* <P>Type: TEXT</P>
|
|
|
|
*/
|
|
|
|
public static final String EXTRAS = "data2";
|
|
|
|
}
|
|
|
|
|
2009-07-05 19:54:49 +00:00
|
|
|
class EasContactsSyncParser extends EasContentParser {
|
|
|
|
|
|
|
|
String[] mBindArgument = new String[1];
|
|
|
|
String mMailboxIdAsString;
|
2009-07-27 17:24:58 +00:00
|
|
|
Uri mAccountUri;
|
2009-07-05 19:54:49 +00:00
|
|
|
|
|
|
|
public EasContactsSyncParser(InputStream in, EasSyncService service) throws IOException {
|
|
|
|
super(in, service);
|
2009-07-27 17:24:58 +00:00
|
|
|
mAccountUri = uriWithAccount(RawContacts.CONTENT_URI);
|
|
|
|
setDebug(false); // DON'T CHECK IN WITH THIS UNCOMMENTED
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
@Override
|
|
|
|
public void wipe() {
|
|
|
|
// TODO Uncomment when the new provider works with this
|
|
|
|
//mContentResolver.delete(mAccountUri, null, null);
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
void saveExtraData (StringBuilder extras, int tag) throws IOException {
|
|
|
|
// TODO Handle containers (categories/children)
|
|
|
|
extras.append(tag);
|
|
|
|
extras.append("~");
|
|
|
|
extras.append(getValue());
|
|
|
|
extras.append('~');
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
class Address {
|
|
|
|
String city;
|
|
|
|
String country;
|
|
|
|
String code;
|
|
|
|
String street;
|
|
|
|
String state;
|
2009-07-05 19:54:49 +00:00
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
boolean hasData() {
|
|
|
|
return city != null || country != null || code != null || state != null
|
|
|
|
|| street != null;
|
|
|
|
}
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
public void addData(String serverId, ContactOperations ops, Entity entity)
|
2009-07-05 19:54:49 +00:00
|
|
|
throws IOException {
|
|
|
|
String firstName = null;
|
|
|
|
String lastName = null;
|
|
|
|
String companyName = null;
|
2009-07-27 17:24:58 +00:00
|
|
|
String title = null;
|
|
|
|
Address home = new Address();
|
|
|
|
Address work = new Address();
|
|
|
|
Address other = new Address();
|
|
|
|
|
|
|
|
if (entity == null) {
|
|
|
|
ops.newContact(serverId);
|
|
|
|
}
|
|
|
|
|
|
|
|
StringBuilder extraData = new StringBuilder(1024);
|
|
|
|
|
2009-07-05 19:54:49 +00:00
|
|
|
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;
|
2009-07-27 17:24:58 +00:00
|
|
|
case EasTags.CONTACTS_JOB_TITLE:
|
|
|
|
title = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_EMAIL1_ADDRESS:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addEmail(entity, TYPE_EMAIL1, getValue());
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_EMAIL2_ADDRESS:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addEmail(entity, TYPE_EMAIL2, getValue());
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_EMAIL3_ADDRESS:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addEmail(entity, TYPE_EMAIL3, getValue());
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, TYPE_WORK2, getValue());
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_BUSINESS_TELEPHONE_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, Phone.TYPE_WORK, getValue());
|
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS2_MMS:
|
|
|
|
ops.addPhone(entity, TYPE_MMS, getValue());
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_BUSINESS_FAX_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, Phone.TYPE_FAX_WORK, getValue());
|
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS2_COMPANY_MAIN_PHONE:
|
|
|
|
ops.addPhone(entity, TYPE_COMPANY_MAIN, getValue());
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_HOME_FAX_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, Phone.TYPE_FAX_HOME, getValue());
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_HOME_TELEPHONE_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, Phone.TYPE_HOME, getValue());
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_HOME2_TELEPHONE_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, TYPE_HOME2, getValue());
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_MOBILE_TELEPHONE_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, Phone.TYPE_MOBILE, getValue());
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_CAR_TELEPHONE_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, TYPE_CAR, getValue());
|
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_RADIO_TELEPHONE_NUMBER:
|
|
|
|
ops.addPhone(entity, TYPE_RADIO, getValue());
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_PAGER_NUMBER:
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addPhone(entity, Phone.TYPE_PAGER, getValue());
|
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS2_IM_ADDRESS:
|
|
|
|
ops.addIm(entity, TYPE_IM1, getValue());
|
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS2_IM_ADDRESS_2:
|
|
|
|
ops.addIm(entity, TYPE_IM2, getValue());
|
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS2_IM_ADDRESS_3:
|
|
|
|
ops.addIm(entity, TYPE_IM3, getValue());
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
case EasTags.CONTACTS_BUSINESS_ADDRESS_CITY:
|
2009-07-27 17:24:58 +00:00
|
|
|
work.city = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_BUSINESS_ADDRESS_COUNTRY:
|
2009-07-27 17:24:58 +00:00
|
|
|
work.country = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_BUSINESS_ADDRESS_POSTAL_CODE:
|
2009-07-27 17:24:58 +00:00
|
|
|
work.code = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_BUSINESS_ADDRESS_STATE:
|
2009-07-27 17:24:58 +00:00
|
|
|
work.state = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_BUSINESS_ADDRESS_STREET:
|
2009-07-27 17:24:58 +00:00
|
|
|
work.street = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_HOME_ADDRESS_CITY:
|
2009-07-27 17:24:58 +00:00
|
|
|
home.city = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_HOME_ADDRESS_COUNTRY:
|
2009-07-27 17:24:58 +00:00
|
|
|
home.country = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_HOME_ADDRESS_POSTAL_CODE:
|
2009-07-27 17:24:58 +00:00
|
|
|
home.code = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_HOME_ADDRESS_STATE:
|
2009-07-27 17:24:58 +00:00
|
|
|
home.state = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_HOME_ADDRESS_STREET:
|
2009-07-27 17:24:58 +00:00
|
|
|
home.street = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_OTHER_ADDRESS_CITY:
|
2009-07-27 17:24:58 +00:00
|
|
|
other.city = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_OTHER_ADDRESS_COUNTRY:
|
2009-07-27 17:24:58 +00:00
|
|
|
other.country = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_OTHER_ADDRESS_POSTAL_CODE:
|
2009-07-27 17:24:58 +00:00
|
|
|
other.code = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_OTHER_ADDRESS_STATE:
|
2009-07-27 17:24:58 +00:00
|
|
|
other.state = getValue();
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_OTHER_ADDRESS_STREET:
|
2009-07-27 17:24:58 +00:00
|
|
|
other.street = getValue();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EasTags.CONTACTS_CHILDREN:
|
|
|
|
childrenParser(extraData);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EasTags.CONTACTS_CATEGORIES:
|
|
|
|
categoriesParser(extraData);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// TODO We'll add this later
|
|
|
|
case EasTags.CONTACTS_PICTURE:
|
|
|
|
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_DEPARTMENT:
|
|
|
|
case EasTags.CONTACTS_FILE_AS:
|
|
|
|
case EasTags.CONTACTS_TITLE:
|
|
|
|
case EasTags.CONTACTS_MIDDLE_NAME:
|
|
|
|
case EasTags.CONTACTS_OFFICE_LOCATION:
|
2009-07-05 19:54:49 +00:00
|
|
|
case EasTags.CONTACTS_SPOUSE:
|
|
|
|
case EasTags.CONTACTS_SUFFIX:
|
|
|
|
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.CONTACTS2_CUSTOMER_ID:
|
|
|
|
case EasTags.CONTACTS2_GOVERNMENT_ID:
|
|
|
|
case EasTags.CONTACTS2_MANAGER_NAME:
|
|
|
|
case EasTags.CONTACTS2_ACCOUNT_NAME:
|
|
|
|
case EasTags.CONTACTS2_NICKNAME:
|
2009-07-27 17:24:58 +00:00
|
|
|
saveExtraData(extraData, tag);
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
skipTag();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addExtras(entity, extraData.toString());
|
2009-07-05 19:54:49 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.addName(entity, firstName, lastName, name);
|
2009-07-05 19:54:49 +00:00
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
if (work.hasData()) {
|
|
|
|
ops.addPostal(entity, StructuredPostal.TYPE_WORK, work.street, work.city,
|
|
|
|
work.state, work.country, work.code);
|
|
|
|
}
|
|
|
|
if (home.hasData()) {
|
|
|
|
ops.addPostal(entity, StructuredPostal.TYPE_HOME, home.street, home.city,
|
|
|
|
home.state, home.country, home.code);
|
|
|
|
}
|
|
|
|
if (other.hasData()) {
|
|
|
|
ops.addPostal(entity, StructuredPostal.TYPE_OTHER, other.street, other.city,
|
|
|
|
other.state, other.country, other.code);
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
if (companyName != null) {
|
|
|
|
ops.addOrganization(entity, Organization.TYPE_WORK, companyName, title);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void categoriesParser(StringBuilder extras) throws IOException {
|
|
|
|
while (nextTag(EasTags.CONTACTS_CATEGORIES) != END) {
|
|
|
|
switch (tag) {
|
|
|
|
case EasTags.CONTACTS_CATEGORY:
|
|
|
|
saveExtraData(extras, tag);
|
|
|
|
default:
|
|
|
|
skipTag();
|
|
|
|
}
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
private void childrenParser(StringBuilder extras) throws IOException {
|
|
|
|
while (nextTag(EasTags.CONTACTS_CHILDREN) != END) {
|
|
|
|
switch (tag) {
|
|
|
|
case EasTags.CONTACTS_CHILD:
|
|
|
|
saveExtraData(extras, tag);
|
|
|
|
default:
|
|
|
|
skipTag();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addParser(ContactOperations ops) throws IOException {
|
2009-07-05 19:54:49 +00:00
|
|
|
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:
|
2009-07-27 17:24:58 +00:00
|
|
|
addData(serverId, ops, null);
|
2009-07-05 19:54:49 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
skipTag();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Cursor getServerIdCursor(String serverId) {
|
|
|
|
mBindArgument[0] = serverId;
|
2009-07-27 17:24:58 +00:00
|
|
|
return mContentResolver.query(mAccountUri, ID_PROJECTION, SERVER_ID_SELECTION,
|
|
|
|
mBindArgument, null);
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
public void deleteParser(ContactOperations ops) throws IOException {
|
2009-07-05 19:54:49 +00:00
|
|
|
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);
|
2009-07-27 17:24:58 +00:00
|
|
|
ops.delete(c.getLong(0));
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
c.close();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
skipTag();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ServerChange {
|
|
|
|
long id;
|
|
|
|
boolean read;
|
|
|
|
|
|
|
|
ServerChange(long _id, boolean _read) {
|
|
|
|
id = _id;
|
|
|
|
read = _read;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-07-27 17:24:58 +00:00
|
|
|
* Changes are handled row by row, and only changed/new rows are acted upon
|
2009-07-05 19:54:49 +00:00
|
|
|
* @param ops the array of pending ContactProviderOperations.
|
|
|
|
* @throws IOException
|
|
|
|
*/
|
2009-07-27 17:24:58 +00:00
|
|
|
public void changeParser(ContactOperations ops) throws IOException {
|
2009-07-05 19:54:49 +00:00
|
|
|
String serverId = null;
|
2009-07-27 17:24:58 +00:00
|
|
|
Entity entity = null;
|
2009-07-05 19:54:49 +00:00
|
|
|
while (nextTag(EasTags.SYNC_CHANGE) != END) {
|
|
|
|
switch (tag) {
|
|
|
|
case EasTags.SYNC_SERVER_ID:
|
|
|
|
serverId = getValue();
|
|
|
|
Cursor c = getServerIdCursor(serverId);
|
|
|
|
try {
|
|
|
|
if (c.moveToFirst()) {
|
2009-07-27 17:24:58 +00:00
|
|
|
// TODO Handle deleted individual rows...
|
|
|
|
try {
|
|
|
|
EntityIterator entityIterator =
|
|
|
|
mContentResolver.queryEntities(ContentUris
|
|
|
|
.withAppendedId(RawContacts.CONTENT_URI, c.getLong(0)),
|
|
|
|
null, null, null);
|
|
|
|
if (entityIterator.hasNext()) {
|
|
|
|
entity = entityIterator.next();
|
|
|
|
}
|
|
|
|
mService.userLog("Changing contact " + serverId);
|
|
|
|
} catch (RemoteException e) {
|
|
|
|
}
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
c.close();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EasTags.SYNC_APPLICATION_DATA:
|
2009-07-27 17:24:58 +00:00
|
|
|
addData(serverId, ops, entity);
|
|
|
|
break;
|
2009-07-05 19:54:49 +00:00
|
|
|
default:
|
|
|
|
skipTag();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
@Override
|
2009-07-05 19:54:49 +00:00
|
|
|
public void commandsParser() throws IOException {
|
2009-07-27 17:24:58 +00:00
|
|
|
ContactOperations ops = new ContactOperations();
|
2009-07-05 19:54:49 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
// Execute these all at once...
|
|
|
|
ops.execute();
|
|
|
|
|
|
|
|
if (ops.mResults != null) {
|
|
|
|
ContentValues cv = new ContentValues();
|
|
|
|
cv.put(RawContacts.DIRTY, 0);
|
|
|
|
for (int i = 0; i < ops.mContactIndexCount; i++) {
|
|
|
|
int index = ops.mContactIndexArray[i];
|
|
|
|
Uri u = ops.mResults[index].uri;
|
|
|
|
if (u != null) {
|
|
|
|
String idString = u.getLastPathSegment();
|
|
|
|
mService.mContentResolver.update(RawContacts.CONTENT_URI, cv,
|
|
|
|
RawContacts._ID + "=" + idString, null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the sync key in the database
|
|
|
|
mService.userLog("Contacts SyncKey saved as: " + mMailbox.mSyncKey);
|
|
|
|
ContentValues cv = new ContentValues();
|
|
|
|
cv.put(MailboxColumns.SYNC_KEY, mMailbox.mSyncKey);
|
|
|
|
Mailbox.update(mContext, Mailbox.CONTENT_URI, mMailbox.mId, cv);
|
2009-07-05 19:54:49 +00:00
|
|
|
|
2009-07-16 18:42:10 +00:00
|
|
|
mService.userLog("Contacts SyncKey confirmed as: " + mMailbox.mSyncKey);
|
2009-07-05 19:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
|
|
|
|
private Uri uriWithAccount(Uri uri) {
|
|
|
|
return uri.buildUpon()
|
|
|
|
.appendQueryParameter(RawContacts.ACCOUNT_NAME, mService.mAccount.mEmailAddress)
|
|
|
|
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, Eas.ACCOUNT_MANAGER_TYPE)
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SmartBuilder is a wrapper for the Builder class that is used to create/update rows for a
|
|
|
|
* ContentProvider. It has, in addition to the Builder, ContentValues which, if present,
|
|
|
|
* represent the current values of that row, that can be compared against current values to
|
|
|
|
* see whether an update is even necessary. The methods on SmartBuilder are delegated to
|
|
|
|
* the Builder.
|
|
|
|
*/
|
|
|
|
private class SmartBuilder {
|
|
|
|
Builder builder;
|
|
|
|
ContentValues cv;
|
|
|
|
|
|
|
|
public SmartBuilder(Builder _builder) {
|
|
|
|
builder = _builder;
|
|
|
|
}
|
|
|
|
|
|
|
|
public SmartBuilder(Builder _builder, NamedContentValues _ncv) {
|
|
|
|
builder = _builder;
|
|
|
|
cv = _ncv.values;
|
|
|
|
}
|
|
|
|
|
|
|
|
SmartBuilder withValues(ContentValues values) {
|
|
|
|
builder.withValues(values);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
SmartBuilder withValueBackReference(String key, int previousResult) {
|
|
|
|
builder.withValueBackReference(key, previousResult);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ContentProviderOperation build() {
|
|
|
|
return builder.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
SmartBuilder withValue(String key, Object value) {
|
|
|
|
builder.withValue(key, value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class ContactOperations extends ArrayList<ContentProviderOperation> {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
private int mCount = 0;
|
|
|
|
private int mContactBackValue = mCount;
|
|
|
|
private int[] mContactIndexArray = new int[10];
|
|
|
|
private int mContactIndexCount = 0;
|
|
|
|
private ContentProviderResult[] mResults = null;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean add(ContentProviderOperation op) {
|
|
|
|
super.add(op);
|
|
|
|
mCount++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void newContact(String serverId) {
|
|
|
|
Builder builder = ContentProviderOperation
|
|
|
|
.newInsert(uriWithAccount(RawContacts.CONTENT_URI));
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
values.put(RawContacts.SOURCE_ID, serverId);
|
|
|
|
builder.withValues(values);
|
|
|
|
mContactBackValue = mCount;
|
|
|
|
mContactIndexArray[mContactIndexCount++] = mCount;
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void delete(long id) {
|
|
|
|
add(ContentProviderOperation.newDelete(ContentUris
|
|
|
|
.withAppendedId(RawContacts.CONTENT_URI, id)).build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void execute() {
|
|
|
|
try {
|
|
|
|
mService.userLog("Executing " + size() + " CPO's");
|
|
|
|
mResults = mService.mContext.getContentResolver()
|
|
|
|
.applyBatch(ContactsContract.AUTHORITY, this);
|
|
|
|
} catch (RemoteException e) {
|
|
|
|
// There is nothing sensible to be done here
|
|
|
|
Log.e(TAG, "problem inserting contact during server update", e);
|
|
|
|
} catch (OperationApplicationException e) {
|
|
|
|
// There is nothing sensible to be done here
|
|
|
|
Log.e(TAG, "problem inserting contact during server update", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the uri for the data row associated with this NamedContentValues object
|
|
|
|
* @param ncv the NamedContentValues object
|
|
|
|
* @return a uri that can be used to refer to this row
|
|
|
|
*/
|
|
|
|
private Uri dataUriFromNamedContentValues(NamedContentValues ncv) {
|
|
|
|
long id = ncv.values.getAsLong(RawContacts._ID);
|
|
|
|
Uri dataUri = ContentUris.withAppendedId(ncv.uri, id);
|
|
|
|
return dataUri;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given the list of NamedContentValues for an entity, a mime type, and a subtype,
|
|
|
|
* tries to find a match, returning it
|
|
|
|
* @param list the list of NCV's from the contact entity
|
|
|
|
* @param contentItemType the mime type we're looking for
|
|
|
|
* @param type the subtype (e.g. HOME, WORK, etc.)
|
|
|
|
* @return the matching NCV or null if not found
|
|
|
|
*/
|
|
|
|
private NamedContentValues findExistingData(ArrayList<NamedContentValues> list,
|
|
|
|
String contentItemType, int type) {
|
|
|
|
NamedContentValues result = null;
|
|
|
|
|
|
|
|
// Loop through the ncv's, looking for an existing row
|
|
|
|
for (NamedContentValues namedContentValues: list) {
|
|
|
|
Uri uri = namedContentValues.uri;
|
|
|
|
ContentValues cv = namedContentValues.values;
|
|
|
|
if (Data.CONTENT_URI.equals(uri)) {
|
|
|
|
String mimeType = cv.getAsString(Data.MIMETYPE);
|
|
|
|
if (mimeType.equals(contentItemType)) {
|
|
|
|
if (type < 0 || cv.getAsInteger(Email.TYPE) == type) {
|
|
|
|
result = namedContentValues;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we've found an existing data row, we'll delete it. Any rows left at the
|
|
|
|
// end should be deleted...
|
|
|
|
if (result != null) {
|
|
|
|
list.remove(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the row found (or null)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a wrapper for a builder (insert or update) that also includes the NCV for
|
|
|
|
* an existing row of this type. If the SmartBuilder's cv field is not null, then
|
|
|
|
* it represents the current (old) values of this field. The caller can then check
|
|
|
|
* whether the field is now different and needs to be updated; if it's not different,
|
|
|
|
* the caller will simply return and not generate a new CPO. Otherwise, the builder
|
|
|
|
* should have its content values set, and the built CPO should be added to the
|
|
|
|
* ContactOperations list.
|
|
|
|
*
|
|
|
|
* @param entity the contact entity (or null if this is a new contact)
|
|
|
|
* @param mimeType the mime type of this row
|
|
|
|
* @param type the subtype of this row
|
|
|
|
* @return the created SmartBuilder
|
|
|
|
*/
|
|
|
|
public SmartBuilder createBuilder(Entity entity, String mimeType, int type) {
|
|
|
|
int contactId = mContactBackValue;
|
|
|
|
SmartBuilder builder = null;
|
|
|
|
|
|
|
|
if (entity != null) {
|
|
|
|
NamedContentValues ncv =
|
|
|
|
findExistingData(entity.getSubValues(), mimeType, type);
|
|
|
|
if (ncv != null) {
|
|
|
|
builder = new SmartBuilder(
|
|
|
|
ContentProviderOperation
|
|
|
|
.newUpdate(dataUriFromNamedContentValues(ncv)),
|
|
|
|
ncv);
|
|
|
|
} else {
|
|
|
|
contactId = entity.getEntityValues().getAsInteger(RawContacts._ID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (builder == null) {
|
|
|
|
builder =
|
|
|
|
new SmartBuilder(ContentProviderOperation.newInsert(Data.CONTENT_URI));
|
|
|
|
if (entity == null) {
|
|
|
|
builder.withValueBackReference(Data.RAW_CONTACT_ID, contactId);
|
|
|
|
} else {
|
|
|
|
builder.withValue(Data.RAW_CONTACT_ID, contactId);
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.withValue(Data.MIMETYPE, mimeType);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the appropriate builder (insert or update)
|
|
|
|
// Caller will fill in the appropriate values; note MIMETYPE is already set
|
|
|
|
return builder;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare a column in a ContentValues with an (old) value, and see if they are the
|
|
|
|
* same. For this purpose, null and an empty string are considered the same.
|
|
|
|
* @param cv a ContentValues object, from a NamedContentValues
|
|
|
|
* @param column a column that might be in the ContentValues
|
|
|
|
* @param oldValue an old value (or null) to check against
|
|
|
|
* @return whether the column's value in the ContentValues matches oldValue
|
|
|
|
*/
|
|
|
|
private boolean cvCompareString(ContentValues cv, String column, String oldValue) {
|
|
|
|
if (cv.containsKey(column)) {
|
|
|
|
if (oldValue != null && cv.getAsString(column).equals(oldValue)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else if (oldValue == null || oldValue.length() == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addEmail(Entity entity, int type, String email) {
|
|
|
|
SmartBuilder builder = createBuilder(entity, Email.CONTENT_ITEM_TYPE, type);
|
|
|
|
ContentValues cv = builder.cv;
|
|
|
|
if (cv != null && cvCompareString(cv, Email.DATA, email)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
builder.withValue(Email.TYPE, type);
|
|
|
|
builder.withValue(Email.DATA, email);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addName(Entity entity, String givenName, String familyName,
|
|
|
|
String displayName) {
|
|
|
|
SmartBuilder builder = createBuilder(entity, StructuredName.CONTENT_ITEM_TYPE, -1);
|
|
|
|
ContentValues cv = builder.cv;
|
|
|
|
if (cv != null && cvCompareString(cv, StructuredName.GIVEN_NAME, givenName) &&
|
|
|
|
cvCompareString(cv, StructuredName.FAMILY_NAME, familyName)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
builder.withValue(StructuredName.GIVEN_NAME, givenName);
|
|
|
|
builder.withValue(StructuredName.FAMILY_NAME, familyName);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addPhoto() {
|
|
|
|
// final int photoRes = Generator.pickRandom(PHOTO_POOL);
|
|
|
|
//
|
|
|
|
// Builder builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
|
|
|
|
// builder.withValueBackReference(Data.CONTACT_ID, 0);
|
|
|
|
// builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
|
|
|
|
// builder.withValue(Photo.PHOTO, getPhotoBytes(photoRes));
|
|
|
|
//
|
|
|
|
// this.add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addPhone(Entity entity, int type, String phone) {
|
|
|
|
SmartBuilder builder = createBuilder(entity, Phone.CONTENT_ITEM_TYPE, type);
|
|
|
|
ContentValues cv = builder.cv;
|
|
|
|
if (cv != null && cvCompareString(cv, Phone.NUMBER, phone)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
builder.withValue(Phone.TYPE, type);
|
|
|
|
builder.withValue(Phone.NUMBER, phone);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addPostal(Entity entity, int type, String street, String city, String state,
|
|
|
|
String country, String code) {
|
|
|
|
SmartBuilder builder = createBuilder(entity, StructuredPostal.CONTENT_ITEM_TYPE,
|
|
|
|
type);
|
|
|
|
ContentValues cv = builder.cv;
|
|
|
|
if (cv != null && cvCompareString(cv, StructuredPostal.CITY, city) &&
|
|
|
|
cvCompareString(cv, StructuredPostal.STREET, street) &&
|
|
|
|
cvCompareString(cv, StructuredPostal.COUNTRY, country) &&
|
|
|
|
cvCompareString(cv, StructuredPostal.POSTCODE, code) &&
|
|
|
|
cvCompareString(cv, StructuredPostal.REGION, state)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
builder.withValue(StructuredPostal.TYPE, type);
|
|
|
|
builder.withValue(StructuredPostal.CITY, city);
|
|
|
|
builder.withValue(StructuredPostal.STREET, street);
|
|
|
|
builder.withValue(StructuredPostal.COUNTRY, country);
|
|
|
|
builder.withValue(StructuredPostal.POSTCODE, code);
|
|
|
|
builder.withValue(StructuredPostal.REGION, state);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addIm(Entity entity, int type, String account) {
|
|
|
|
SmartBuilder builder = createBuilder(entity, Im.CONTENT_ITEM_TYPE, type);
|
|
|
|
ContentValues cv = builder.cv;
|
|
|
|
if (cv != null && cvCompareString(cv, Im.DATA, account)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
builder.withValue(Im.TYPE, type);
|
|
|
|
builder.withValue(Im.DATA, account);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addOrganization(Entity entity, int type, String company, String title) {
|
|
|
|
SmartBuilder builder = createBuilder(entity, Organization.CONTENT_ITEM_TYPE, type);
|
|
|
|
ContentValues cv = builder.cv;
|
|
|
|
if (cv != null && cvCompareString(cv, Organization.COMPANY, company) &&
|
|
|
|
cvCompareString(cv, Organization.TITLE, title)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
builder.withValue(Organization.TYPE, type);
|
|
|
|
builder.withValue(Organization.COMPANY, company);
|
|
|
|
builder.withValue(Organization.TITLE, title);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
public void addNote(String note) {
|
|
|
|
Builder builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
|
|
|
|
builder.withValueBackReference(Data.RAW_CONTACT_ID, mContactBackValue);
|
|
|
|
builder.withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
|
|
|
|
builder.withValue(Note.NOTE, note);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addExtras(Entity entity, String extras) {
|
|
|
|
SmartBuilder builder = createBuilder(entity, Extras.CONTENT_ITEM_TYPE, -1);
|
|
|
|
ContentValues cv = builder.cv;
|
|
|
|
if (cv != null && cvCompareString(cv, Extras.EXTRAS, extras)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
builder.withValue(Extras.EXTRAS, extras);
|
|
|
|
add(builder.build());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-15 00:21:13 +00:00
|
|
|
@Override
|
|
|
|
public void cleanup(EasSyncService service) {
|
2009-07-27 17:24:58 +00:00
|
|
|
// Mark the changed contacts dirty = 0
|
|
|
|
// TODO Put this in a single batch
|
|
|
|
ContactOperations ops = new ContactOperations();
|
|
|
|
for (Long id: mUpdatedIdList) {
|
|
|
|
ops.add(ContentProviderOperation
|
|
|
|
.newUpdate(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id))
|
|
|
|
.withValue(RawContacts.DIRTY, 0).build());
|
|
|
|
}
|
|
|
|
|
|
|
|
ops.execute();
|
2009-07-15 00:21:13 +00:00
|
|
|
}
|
|
|
|
|
2009-07-05 19:54:49 +00:00
|
|
|
@Override
|
|
|
|
public String getCollectionName() {
|
|
|
|
return "Contacts";
|
|
|
|
}
|
|
|
|
|
2009-07-27 17:24:58 +00:00
|
|
|
private void sendEmail(EasSerializer s, ContentValues cv) throws IOException {
|
|
|
|
String value = cv.getAsString(Email.DATA);
|
|
|
|
switch (cv.getAsInteger(Email.TYPE)) {
|
|
|
|
case TYPE_EMAIL1:
|
|
|
|
s.data("Email1Address", value);
|
|
|
|
break;
|
|
|
|
case TYPE_EMAIL2:
|
|
|
|
s.data("Email2Address", value);
|
|
|
|
break;
|
|
|
|
case TYPE_EMAIL3:
|
|
|
|
s.data("Email3Address", value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendIm(EasSerializer s, ContentValues cv) throws IOException {
|
|
|
|
String value = cv.getAsString(Email.DATA);
|
|
|
|
switch (cv.getAsInteger(Email.TYPE)) {
|
|
|
|
case TYPE_IM1:
|
|
|
|
s.data("IMAddress", value);
|
|
|
|
break;
|
|
|
|
case TYPE_IM2:
|
|
|
|
s.data("IMAddress2", value);
|
|
|
|
break;
|
|
|
|
case TYPE_IM3:
|
|
|
|
s.data("IMAddress3", value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendOnePostal(EasSerializer s, ContentValues cv, String[] fieldNames)
|
|
|
|
throws IOException{
|
|
|
|
if (cv.containsKey(StructuredPostal.CITY)) {
|
|
|
|
s.data(fieldNames[0], cv.getAsString(StructuredPostal.CITY));
|
|
|
|
}
|
|
|
|
if (cv.containsKey(StructuredPostal.COUNTRY)) {
|
|
|
|
s.data(fieldNames[1], cv.getAsString(StructuredPostal.COUNTRY));
|
|
|
|
}
|
|
|
|
if (cv.containsKey(StructuredPostal.POSTCODE)) {
|
|
|
|
s.data(fieldNames[2], cv.getAsString(StructuredPostal.POSTCODE));
|
|
|
|
}
|
|
|
|
if (cv.containsKey(StructuredPostal.REGION)) {
|
|
|
|
s.data(fieldNames[3], cv.getAsString(StructuredPostal.REGION));
|
|
|
|
}
|
|
|
|
if (cv.containsKey(StructuredPostal.STREET)) {
|
|
|
|
s.data(fieldNames[4], cv.getAsString(StructuredPostal.STREET));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendStructuredPostal(EasSerializer s, ContentValues cv) throws IOException {
|
|
|
|
switch (cv.getAsInteger(StructuredPostal.TYPE)) {
|
|
|
|
case StructuredPostal.TYPE_HOME:
|
|
|
|
sendOnePostal(s, cv, new String[] {"HomeAddressCity", "HomeAddressCountry",
|
|
|
|
"HomeAddressPostalCode", "HomeAddressState", "HomeAddressStreet"});
|
|
|
|
break;
|
|
|
|
case StructuredPostal.TYPE_WORK:
|
|
|
|
sendOnePostal(s, cv, new String[] {"BusinessAddressCity", "BusinessAddressCountry",
|
|
|
|
"BusinessAddressPostalCode", "BusinessAddressState",
|
|
|
|
"BusinessAddressStreet"});
|
|
|
|
break;
|
|
|
|
case StructuredPostal.TYPE_OTHER:
|
|
|
|
sendOnePostal(s, cv, new String[] {"OtherAddressCity", "OtherAddressCountry",
|
|
|
|
"OtherAddressPostalCode", "OtherAddressState", "OtherAddressStreet"});
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendStructuredName(EasSerializer s, ContentValues cv) throws IOException {
|
|
|
|
if (cv.containsKey(StructuredName.FAMILY_NAME)) {
|
|
|
|
s.data("LastName", cv.getAsString(StructuredName.FAMILY_NAME));
|
|
|
|
}
|
|
|
|
if (cv.containsKey(StructuredName.GIVEN_NAME)) {
|
|
|
|
s.data("FirstName", cv.getAsString(StructuredName.GIVEN_NAME));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendOrganization(EasSerializer s, ContentValues cv) throws IOException {
|
|
|
|
if (cv.containsKey(Organization.TITLE)) {
|
|
|
|
s.data("JobTitle", cv.getAsString(Organization.TITLE));
|
|
|
|
}
|
|
|
|
if (cv.containsKey(Organization.COMPANY)) {
|
|
|
|
s.data("CompanyName", cv.getAsString(Organization.COMPANY));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void sendPhone(EasSerializer s, ContentValues cv) throws IOException {
|
|
|
|
String value = cv.getAsString(Phone.NUMBER);
|
|
|
|
switch (cv.getAsInteger(Phone.TYPE)) {
|
|
|
|
case TYPE_WORK2:
|
|
|
|
s.data("Business2TelephoneNumber", value);
|
|
|
|
break;
|
|
|
|
case Phone.TYPE_WORK:
|
|
|
|
s.data("BusinessTelephoneNumber", value);
|
|
|
|
break;
|
|
|
|
case TYPE_MMS:
|
|
|
|
s.data("MMS", value);
|
|
|
|
break;
|
|
|
|
case Phone.TYPE_FAX_WORK:
|
|
|
|
s.data("BusinessFaxNumber", value);
|
|
|
|
break;
|
|
|
|
case TYPE_COMPANY_MAIN:
|
|
|
|
s.data("CompanyMainPhone", value);
|
|
|
|
break;
|
|
|
|
case Phone.TYPE_HOME:
|
|
|
|
s.data("HomeTelephoneNumber", value);
|
|
|
|
break;
|
|
|
|
case TYPE_HOME2:
|
|
|
|
s.data("Home2TelephoneNumber", value);
|
|
|
|
break;
|
|
|
|
case Phone.TYPE_MOBILE:
|
|
|
|
s.data("MobileTelephoneNumber", value);
|
|
|
|
break;
|
|
|
|
case TYPE_CAR:
|
|
|
|
s.data("CarTelephoneNumber", value);
|
|
|
|
break;
|
|
|
|
case Phone.TYPE_PAGER:
|
|
|
|
s.data("PagerNumber", value);
|
|
|
|
break;
|
|
|
|
case TYPE_RADIO:
|
|
|
|
s.data("RadioTelephoneNumber", value);
|
|
|
|
break;
|
|
|
|
case Phone.TYPE_FAX_HOME:
|
|
|
|
s.data("HomeFaxNumber", value);
|
|
|
|
break;
|
|
|
|
case TYPE_EMAIL2:
|
|
|
|
s.data("Email2Address", value);
|
|
|
|
break;
|
|
|
|
case TYPE_EMAIL3:
|
|
|
|
s.data("Email3Address", value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-05 19:54:49 +00:00
|
|
|
@Override
|
|
|
|
public boolean sendLocalChanges(EasSerializer s, EasSyncService service) throws IOException {
|
2009-07-27 17:24:58 +00:00
|
|
|
// First, let's find Contacts that have changed.
|
|
|
|
ContentResolver cr = service.mContentResolver;
|
|
|
|
Uri uri = RawContacts.CONTENT_URI.buildUpon()
|
|
|
|
.appendQueryParameter(RawContacts.ACCOUNT_NAME, service.mAccount.mEmailAddress)
|
|
|
|
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, Eas.ACCOUNT_MANAGER_TYPE)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Get them all atomically
|
|
|
|
//EntityIterator ei = cr.queryEntities(uri, RawContacts.DIRTY + "=1", null, null);
|
|
|
|
EntityIterator ei = cr.queryEntities(uri, null, null, null);
|
|
|
|
boolean first = true;
|
|
|
|
while (ei.hasNext()) {
|
|
|
|
Entity entity = ei.next();
|
|
|
|
// For each of these entities, create the change commands
|
|
|
|
ContentValues entityValues = entity.getEntityValues();
|
|
|
|
String serverId = entityValues.getAsString(RawContacts.SOURCE_ID);
|
|
|
|
if (first) {
|
|
|
|
s.start("Commands");
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
s.start("Change").data("ServerId", serverId).start("ApplicationData");
|
|
|
|
// Write out the data here
|
|
|
|
for (NamedContentValues ncv: entity.getSubValues()) {
|
|
|
|
ContentValues cv = ncv.values;
|
|
|
|
String mimeType = cv.getAsString(Data.MIMETYPE);
|
|
|
|
if (mimeType.equals(Email.CONTENT_ITEM_TYPE)) {
|
|
|
|
sendEmail(s, cv);
|
|
|
|
} else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) {
|
|
|
|
sendPhone(s, cv);
|
|
|
|
} else if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) {
|
|
|
|
sendStructuredName(s, cv);
|
|
|
|
} else if (mimeType.equals(StructuredPostal.CONTENT_ITEM_TYPE)) {
|
|
|
|
sendStructuredPostal(s, cv);
|
|
|
|
} else if (mimeType.equals(Organization.CONTENT_ITEM_TYPE)) {
|
|
|
|
sendOrganization(s, cv);
|
|
|
|
} else if (mimeType.equals(Im.CONTENT_ITEM_TYPE)) {
|
|
|
|
sendIm(s, cv);
|
|
|
|
} else if (mimeType.equals(Note.CONTENT_ITEM_TYPE)) {
|
|
|
|
|
|
|
|
} else if (mimeType.equals(Extras.CONTENT_ITEM_TYPE)) {
|
|
|
|
|
|
|
|
} else {
|
|
|
|
mService.userLog("Contacts upsync, unknown data: " + mimeType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.end("ApplicationData").end("Change");
|
|
|
|
mUpdatedIdList.add(entityValues.getAsLong(RawContacts._ID));
|
|
|
|
}
|
|
|
|
if (!first) {
|
|
|
|
s.end("Commands");
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (RemoteException e) {
|
|
|
|
Log.e(TAG, "Could not read dirty contacts.");
|
|
|
|
}
|
|
|
|
|
2009-07-05 19:54:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|