Merge change 23483 into eclair

* changes:
  Rework ContactsSyncAdapter to handle untyped Email and IM data
This commit is contained in:
Android (Google) Code Review 2009-09-09 18:49:54 -04:00
commit d46ec14e6e

View File

@ -75,6 +75,8 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
private static final String[] ID_PROJECTION = new String[] {RawContacts._ID}; private static final String[] ID_PROJECTION = new String[] {RawContacts._ID};
private static final String[] GROUP_PROJECTION = new String[] {Groups.SOURCE_ID}; private static final String[] GROUP_PROJECTION = new String[] {Groups.SOURCE_ID};
private static final String FOUND_DATA_ROW = "com.android.exchange.FOUND_ROW";
private static final int[] HOME_ADDRESS_TAGS = new int[] {Tags.CONTACTS_HOME_ADDRESS_CITY, private static final int[] HOME_ADDRESS_TAGS = new int[] {Tags.CONTACTS_HOME_ADDRESS_CITY,
Tags.CONTACTS_HOME_ADDRESS_COUNTRY, Tags.CONTACTS_HOME_ADDRESS_COUNTRY,
Tags.CONTACTS_HOME_ADDRESS_POSTAL_CODE, Tags.CONTACTS_HOME_ADDRESS_POSTAL_CODE,
@ -93,27 +95,19 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
Tags.CONTACTS_OTHER_ADDRESS_STATE, Tags.CONTACTS_OTHER_ADDRESS_STATE,
Tags.CONTACTS_OTHER_ADDRESS_STREET}; Tags.CONTACTS_OTHER_ADDRESS_STREET};
// Note: These constants are likely to change; they are internal to this class now, but private static final int MAX_IM_ROWS = 3;
// may end up in the provider. private static final int MAX_EMAIL_ROWS = 3;
private static final int TYPE_EMAIL1 = 20; private static final String COMMON_DATA_ROW = Im.DATA; // Could have been Email.DATA, etc.
private static final int TYPE_EMAIL2 = 21;
private static final int TYPE_EMAIL3 = 22;
// We'll split email into two columns, the one that Contacts uses (just for the email address private static final int[] IM_TAGS = new int[] {Tags.CONTACTS2_IM_ADDRESS,
// portion, and another one (the one defined here) for the display name Tags.CONTACTS2_IM_ADDRESS_2, Tags.CONTACTS2_IM_ADDRESS_3};
//private static final String EMAIL_DISPLAY_NAME = Data.SYNC1;
private static final int TYPE_IM1 = 23; private static final int[] EMAIL_TAGS = new int[] {Tags.CONTACTS_EMAIL1_ADDRESS,
private static final int TYPE_IM2 = 24; Tags.CONTACTS_EMAIL2_ADDRESS, Tags.CONTACTS_EMAIL3_ADDRESS};
private static final int TYPE_IM3 = 25;
private static final int TYPE_WORK2 = 26; private static final int TYPE_WORK2 = 26;
private static final int TYPE_HOME2 = 27; 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_MMS = 30;
private static final int TYPE_RADIO = 31;
private static final int TYPE_ASSISTANT = 32;
ArrayList<Long> mDeletedIdList = new ArrayList<Long>(); ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>(); ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
@ -130,6 +124,11 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
return p.parse(); return p.parse();
} }
interface UntypedRow {
public void addValues(RowBuilder builder);
public boolean isSameAs(String value);
}
/** /**
* We get our SyncKey from ContactsProvider. If there's not one, we set it to "0" (the reset * We get our SyncKey from ContactsProvider. If there's not one, we set it to "0" (the reset
* state) and save that away. * state) and save that away.
@ -248,7 +247,50 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
} }
class EasContactsSyncParser extends AbstractSyncParser { class EmailRow implements UntypedRow {
String email;
String displayName;
public EmailRow(String _email) {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(_email);
// Can't happen, but belt & suspenders
if (tokens.length == 0) {
email = "";
displayName = "";
} else {
Rfc822Token token = tokens[0];
email = token.getAddress();
displayName = token.getName();
}
}
public void addValues(RowBuilder builder) {
builder.withValue(Email.DATA, email);
builder.withValue(Email.DISPLAY_NAME, displayName);
}
public boolean isSameAs(String value) {
return email.equalsIgnoreCase(value);
}
}
class ImRow implements UntypedRow {
String im;
public ImRow(String _im) {
im = _im;
}
public void addValues(RowBuilder builder) {
builder.withValue(Im.DATA, im);
}
public boolean isSameAs(String value) {
return im.equalsIgnoreCase(value);
}
}
class EasContactsSyncParser extends AbstractSyncParser {
String[] mBindArgument = new String[1]; String[] mBindArgument = new String[1];
String mMailboxIdAsString; String mMailboxIdAsString;
@ -286,7 +328,8 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
EasBusiness business = new EasBusiness(); EasBusiness business = new EasBusiness();
EasPersonal personal = new EasPersonal(); EasPersonal personal = new EasPersonal();
ArrayList<String> children = new ArrayList<String>(); ArrayList<String> children = new ArrayList<String>();
ArrayList<UntypedRow> emails = new ArrayList<UntypedRow>();
ArrayList<UntypedRow> ims = new ArrayList<UntypedRow>();
if (entity == null) { if (entity == null) {
ops.newContact(serverId); ops.newContact(serverId);
} }
@ -312,13 +355,9 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
title = getValue(); title = getValue();
break; break;
case Tags.CONTACTS_EMAIL1_ADDRESS: case Tags.CONTACTS_EMAIL1_ADDRESS:
ops.addEmail(entity, TYPE_EMAIL1, getValue());
break;
case Tags.CONTACTS_EMAIL2_ADDRESS: case Tags.CONTACTS_EMAIL2_ADDRESS:
ops.addEmail(entity, TYPE_EMAIL2, getValue());
break;
case Tags.CONTACTS_EMAIL3_ADDRESS: case Tags.CONTACTS_EMAIL3_ADDRESS:
ops.addEmail(entity, TYPE_EMAIL3, getValue()); emails.add(new EmailRow(getValue()));
break; break;
case Tags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER: case Tags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER:
ops.addPhone(entity, TYPE_WORK2, getValue()); ops.addPhone(entity, TYPE_WORK2, getValue());
@ -333,7 +372,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
ops.addPhone(entity, Phone.TYPE_FAX_WORK, getValue()); ops.addPhone(entity, Phone.TYPE_FAX_WORK, getValue());
break; break;
case Tags.CONTACTS2_COMPANY_MAIN_PHONE: case Tags.CONTACTS2_COMPANY_MAIN_PHONE:
ops.addPhone(entity, TYPE_COMPANY_MAIN, getValue()); ops.addPhone(entity, Phone.TYPE_COMPANY_MAIN, getValue());
break; break;
case Tags.CONTACTS_HOME_FAX_NUMBER: case Tags.CONTACTS_HOME_FAX_NUMBER:
ops.addPhone(entity, Phone.TYPE_FAX_HOME, getValue()); ops.addPhone(entity, Phone.TYPE_FAX_HOME, getValue());
@ -348,25 +387,21 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
ops.addPhone(entity, Phone.TYPE_MOBILE, getValue()); ops.addPhone(entity, Phone.TYPE_MOBILE, getValue());
break; break;
case Tags.CONTACTS_CAR_TELEPHONE_NUMBER: case Tags.CONTACTS_CAR_TELEPHONE_NUMBER:
ops.addPhone(entity, TYPE_CAR, getValue()); ops.addPhone(entity, Phone.TYPE_CAR, getValue());
break; break;
case Tags.CONTACTS_RADIO_TELEPHONE_NUMBER: case Tags.CONTACTS_RADIO_TELEPHONE_NUMBER:
ops.addPhone(entity, TYPE_RADIO, getValue()); ops.addPhone(entity, Phone.TYPE_RADIO, getValue());
break; break;
case Tags.CONTACTS_PAGER_NUMBER: case Tags.CONTACTS_PAGER_NUMBER:
ops.addPhone(entity, Phone.TYPE_PAGER, getValue()); ops.addPhone(entity, Phone.TYPE_PAGER, getValue());
break; break;
case Tags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER: case Tags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER:
ops.addPhone(entity, TYPE_ASSISTANT, getValue()); ops.addPhone(entity, Phone.TYPE_ASSISTANT, getValue());
break; break;
case Tags.CONTACTS2_IM_ADDRESS: case Tags.CONTACTS2_IM_ADDRESS:
ops.addIm(entity, TYPE_IM1, getValue());
break;
case Tags.CONTACTS2_IM_ADDRESS_2: case Tags.CONTACTS2_IM_ADDRESS_2:
ops.addIm(entity, TYPE_IM2, getValue());
break;
case Tags.CONTACTS2_IM_ADDRESS_3: case Tags.CONTACTS2_IM_ADDRESS_3:
ops.addIm(entity, TYPE_IM3, getValue()); ims.add(new ImRow(getValue()));
break; break;
case Tags.CONTACTS_BUSINESS_ADDRESS_CITY: case Tags.CONTACTS_BUSINESS_ADDRESS_CITY:
work.city = getValue(); work.city = getValue();
@ -526,6 +561,9 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
ops.addBusiness(entity, business); ops.addBusiness(entity, business);
ops.addPersonal(entity, personal); ops.addPersonal(entity, personal);
ops.addUntyped(entity, emails, Email.CONTENT_ITEM_TYPE, MAX_EMAIL_ROWS);
ops.addUntyped(entity, ims, Im.CONTENT_ITEM_TYPE, MAX_IM_ROWS);
if (!children.isEmpty()) { if (!children.isEmpty()) {
ops.addChildren(entity, children); ops.addChildren(entity, children);
} }
@ -809,25 +847,25 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
* see whether an update is even necessary. The methods on SmartBuilder are delegated to * see whether an update is even necessary. The methods on SmartBuilder are delegated to
* the Builder. * the Builder.
*/ */
private class SmartBuilder { private class RowBuilder {
Builder builder; Builder builder;
ContentValues cv; ContentValues cv;
public SmartBuilder(Builder _builder) { public RowBuilder(Builder _builder) {
builder = _builder; builder = _builder;
} }
public SmartBuilder(Builder _builder, NamedContentValues _ncv) { public RowBuilder(Builder _builder, NamedContentValues _ncv) {
builder = _builder; builder = _builder;
cv = _ncv.values; cv = _ncv.values;
} }
SmartBuilder withValues(ContentValues values) { RowBuilder withValues(ContentValues values) {
builder.withValues(values); builder.withValues(values);
return this; return this;
} }
SmartBuilder withValueBackReference(String key, int previousResult) { RowBuilder withValueBackReference(String key, int previousResult) {
builder.withValueBackReference(key, previousResult); builder.withValueBackReference(key, previousResult);
return this; return this;
} }
@ -836,7 +874,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
return builder.build(); return builder.build();
} }
SmartBuilder withValue(String key, Object value) { RowBuilder withValue(String key, Object value) {
builder.withValue(key, value); builder.withValue(key, value);
return this; return this;
} }
@ -906,7 +944,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
* @param type the subtype (e.g. HOME, WORK, etc.) * @param type the subtype (e.g. HOME, WORK, etc.)
* @return the matching NCV or null if not found * @return the matching NCV or null if not found
*/ */
private NamedContentValues findExistingData(ArrayList<NamedContentValues> list, private NamedContentValues findTypedData(ArrayList<NamedContentValues> list,
String contentItemType, int type, String stringType) { String contentItemType, int type, String stringType) {
NamedContentValues result = null; NamedContentValues result = null;
@ -930,7 +968,40 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
} }
// TODO Handle deleted items // 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;
}
/**
* Given the list of NamedContentValues for an entity and a mime type
* gather all of the matching NCV's, returning them
* @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 NCVs
*/
private ArrayList<NamedContentValues> findUntypedData(ArrayList<NamedContentValues> list,
String contentItemType) {
ArrayList<NamedContentValues> result = new ArrayList<NamedContentValues>();
// 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)) {
result.add(namedContentValues);
}
}
}
// If we've found an existing data row, we'll delete it. Any rows left at the // If we've found an existing data row, we'll delete it. Any rows left at the
// end should be deleted... // end should be deleted...
if (result != null) { if (result != null) {
@ -956,38 +1027,23 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
* @param stringType for groups, the name of the group (type will be ignored), or null * @param stringType for groups, the name of the group (type will be ignored), or null
* @return the created SmartBuilder * @return the created SmartBuilder
*/ */
public SmartBuilder createBuilder(Entity entity, String mimeType, int type) { public RowBuilder createBuilder(Entity entity, String mimeType, int type,
return createBuilder(entity, mimeType, type, null);
}
public SmartBuilder createBuilder(Entity entity, String mimeType, int type,
String stringType) { String stringType) {
int contactId = mContactBackValue; RowBuilder builder = null;
SmartBuilder builder = null;
if (entity != null) { if (entity != null) {
NamedContentValues ncv = NamedContentValues ncv =
findExistingData(entity.getSubValues(), mimeType, type, stringType); findTypedData(entity.getSubValues(), mimeType, type, stringType);
if (ncv != null) { if (ncv != null) {
builder = new SmartBuilder( builder = new RowBuilder(
ContentProviderOperation ContentProviderOperation
.newUpdate(dataUriFromNamedContentValues(ncv)), .newUpdate(dataUriFromNamedContentValues(ncv)),
ncv); ncv);
} else {
contactId = entity.getEntityValues().getAsInteger(RawContacts._ID);
} }
} }
if (builder == null) { if (builder == null) {
builder = builder = newRowBuilder(entity, mimeType);
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) // Return the appropriate builder (insert or update)
@ -995,6 +1051,36 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
return builder; return builder;
} }
private RowBuilder typedRowBuilder(Entity entity, String mimeType, int type) {
return createBuilder(entity, mimeType, type, null);
}
private RowBuilder untypedRowBuilder(Entity entity, String mimeType) {
return createBuilder(entity, mimeType, -1, null);
}
private RowBuilder newRowBuilder(Entity entity, String mimeType) {
// This is a new row; first get the contactId
// If the Contact is new, use the saved back value; otherwise the value in the entity
int contactId = mContactBackValue;
if (entity != null) {
contactId = entity.getEntityValues().getAsInteger(RawContacts._ID);
}
// Create an insert operation with the proper contactId reference
RowBuilder builder =
new RowBuilder(ContentProviderOperation.newInsert(Data.CONTENT_URI));
if (entity == null) {
builder.withValueBackReference(Data.RAW_CONTACT_ID, contactId);
} else {
builder.withValue(Data.RAW_CONTACT_ID, contactId);
}
// Set the mime type of the row
builder.withValue(Data.MIMETYPE, mimeType);
return builder;
}
/** /**
* Compare a column in a ContentValues with an (old) value, and see if they are the * 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. * same. For this purpose, null and an empty string are considered the same.
@ -1014,29 +1100,8 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
return false; return false;
} }
public void addEmail(Entity entity, int type, String email) {
SmartBuilder builder = createBuilder(entity, Email.CONTENT_ITEM_TYPE, type);
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
// Can't happen, but belt & suspenders
if (tokens.length == 0) {
return;
}
Rfc822Token token = tokens[0];
String addr = token.getAddress();
String name = token.getName();
ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, Email.DATA, addr)
&& cvCompareString(cv, Email.DISPLAY_NAME, name)) {
return;
}
builder.withValue(Email.TYPE, type);
builder.withValue(Email.DATA, addr);
builder.withValue(Email.DISPLAY_NAME, name);
add(builder.build());
}
public void addChildren(Entity entity, ArrayList<String> children) { public void addChildren(Entity entity, ArrayList<String> children) {
SmartBuilder builder = createBuilder(entity, EasChildren.CONTENT_ITEM_TYPE, -1); RowBuilder builder = untypedRowBuilder(entity, EasChildren.CONTENT_ITEM_TYPE);
int i = 0; int i = 0;
for (String child: children) { for (String child: children) {
builder.withValue(EasChildren.ROWS[i++], child); builder.withValue(EasChildren.ROWS[i++], child);
@ -1045,7 +1110,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addGroup(Entity entity, String group) { public void addGroup(Entity entity, String group) {
SmartBuilder builder = RowBuilder builder =
createBuilder(entity, GroupMembership.CONTENT_ITEM_TYPE, -1, group); createBuilder(entity, GroupMembership.CONTENT_ITEM_TYPE, -1, group);
builder.withValue(GroupMembership.GROUP_SOURCE_ID, group); builder.withValue(GroupMembership.GROUP_SOURCE_ID, group);
add(builder.build()); add(builder.build());
@ -1054,7 +1119,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
public void addName(Entity entity, String prefix, String givenName, String familyName, public void addName(Entity entity, String prefix, String givenName, String familyName,
String middleName, String suffix, String displayName, String yomiFirstName, String middleName, String suffix, String displayName, String yomiFirstName,
String yomiLastName) { String yomiLastName) {
SmartBuilder builder = createBuilder(entity, StructuredName.CONTENT_ITEM_TYPE, -1); RowBuilder builder = untypedRowBuilder(entity, StructuredName.CONTENT_ITEM_TYPE);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, StructuredName.GIVEN_NAME, givenName) && if (cv != null && cvCompareString(cv, StructuredName.GIVEN_NAME, givenName) &&
cvCompareString(cv, StructuredName.FAMILY_NAME, familyName) && cvCompareString(cv, StructuredName.FAMILY_NAME, familyName) &&
@ -1076,7 +1141,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addPersonal(Entity entity, EasPersonal personal) { public void addPersonal(Entity entity, EasPersonal personal) {
SmartBuilder builder = createBuilder(entity, EasPersonal.CONTENT_ITEM_TYPE, -1); RowBuilder builder = untypedRowBuilder(entity, EasPersonal.CONTENT_ITEM_TYPE);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, EasPersonal.ANNIVERSARY, personal.anniversary) && if (cv != null && cvCompareString(cv, EasPersonal.ANNIVERSARY, personal.anniversary) &&
cvCompareString(cv, EasPersonal.BIRTHDAY, personal.birthday) && cvCompareString(cv, EasPersonal.BIRTHDAY, personal.birthday) &&
@ -1093,7 +1158,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addBusiness(Entity entity, EasBusiness business) { public void addBusiness(Entity entity, EasBusiness business) {
SmartBuilder builder = createBuilder(entity, EasBusiness.CONTENT_ITEM_TYPE, -1); RowBuilder builder = untypedRowBuilder(entity, EasBusiness.CONTENT_ITEM_TYPE);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, EasBusiness.ACCOUNT_NAME, business.accountName) && if (cv != null && cvCompareString(cv, EasBusiness.ACCOUNT_NAME, business.accountName) &&
cvCompareString(cv, EasBusiness.CUSTOMER_ID, business.customerId) && cvCompareString(cv, EasBusiness.CUSTOMER_ID, business.customerId) &&
@ -1112,7 +1177,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addPhoto(Entity entity, String photo) { public void addPhoto(Entity entity, String photo) {
SmartBuilder builder = createBuilder(entity, Photo.CONTENT_ITEM_TYPE, -1); RowBuilder builder = untypedRowBuilder(entity, Photo.CONTENT_ITEM_TYPE);
// We're always going to add this; it's not worth trying to figure out whether the // We're always going to add this; it's not worth trying to figure out whether the
// picture is the same as the one stored. // picture is the same as the one stored.
byte[] pic = Base64.decodeBase64(photo.getBytes()); byte[] pic = Base64.decodeBase64(photo.getBytes());
@ -1121,7 +1186,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addPhone(Entity entity, int type, String phone) { public void addPhone(Entity entity, int type, String phone) {
SmartBuilder builder = createBuilder(entity, Phone.CONTENT_ITEM_TYPE, type); RowBuilder builder = typedRowBuilder(entity, Phone.CONTENT_ITEM_TYPE, type);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, Phone.NUMBER, phone)) { if (cv != null && cvCompareString(cv, Phone.NUMBER, phone)) {
return; return;
@ -1132,7 +1197,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addWebpage(Entity entity, String url) { public void addWebpage(Entity entity, String url) {
SmartBuilder builder = createBuilder(entity, Website.CONTENT_ITEM_TYPE, -1); RowBuilder builder = untypedRowBuilder(entity, Website.CONTENT_ITEM_TYPE);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, Website.URL, url)) { if (cv != null && cvCompareString(cv, Website.URL, url)) {
return; return;
@ -1143,7 +1208,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addRelation(Entity entity, int type, String value) { public void addRelation(Entity entity, int type, String value) {
SmartBuilder builder = createBuilder(entity, Relation.CONTENT_ITEM_TYPE, type); RowBuilder builder = typedRowBuilder(entity, Relation.CONTENT_ITEM_TYPE, type);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, Relation.DATA, value)) { if (cv != null && cvCompareString(cv, Relation.DATA, value)) {
return; return;
@ -1154,8 +1219,8 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addNickname(Entity entity, String name) { public void addNickname(Entity entity, String name) {
SmartBuilder builder = RowBuilder builder =
createBuilder(entity, Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE_DEFAULT); typedRowBuilder(entity, Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE_DEFAULT);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, Nickname.NAME, name)) { if (cv != null && cvCompareString(cv, Nickname.NAME, name)) {
return; return;
@ -1167,7 +1232,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
public void addPostal(Entity entity, int type, String street, String city, String state, public void addPostal(Entity entity, int type, String street, String city, String state,
String country, String code) { String country, String code) {
SmartBuilder builder = createBuilder(entity, StructuredPostal.CONTENT_ITEM_TYPE, RowBuilder builder = typedRowBuilder(entity, StructuredPostal.CONTENT_ITEM_TYPE,
type); type);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, StructuredPostal.CITY, city) && if (cv != null && cvCompareString(cv, StructuredPostal.CITY, city) &&
@ -1186,20 +1251,73 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
add(builder.build()); add(builder.build());
} }
public void addIm(Entity entity, int type, String account) { /**
SmartBuilder builder = createBuilder(entity, Im.CONTENT_ITEM_TYPE, type); * We now are dealing with up to maxRows typeless rows of mimeType data. We need to try to
ContentValues cv = builder.cv; * match them with existing rows; if there's a match, everything's great. Otherwise, we
if (cv != null && cvCompareString(cv, Im.DATA, account)) { * either need to add a new row for the data, or we have to replace an existing one
return; * that no longer matches. This is similar to the way Emails are handled.
*/
public void addUntyped(Entity entity, ArrayList<UntypedRow> rows, String mimeType,
int maxRows) {
// Make a list of all same type rows in the existing entity
ArrayList<NamedContentValues> oldAccounts = new ArrayList<NamedContentValues>();
if (entity != null) {
oldAccounts = findUntypedData(entity.getSubValues(), mimeType);
}
// These will be rows needing replacement with new values
ArrayList<UntypedRow> rowsToReplace = new ArrayList<UntypedRow>();
// The count of existing rows
int numRows = oldAccounts.size();
for (UntypedRow row: rows) {
boolean found = false;
// If we already have this IM address, mark it
for (NamedContentValues ncv: oldAccounts) {
ContentValues cv = ncv.values;
String data = cv.getAsString(COMMON_DATA_ROW);
if (row.isSameAs(data)) {
cv.put(FOUND_DATA_ROW, true);
found = true;
break;
}
}
if (!found) {
// If we don't, there are two possibilities
if (numRows < maxRows) {
// If there are available rows, add a new one
RowBuilder builder = newRowBuilder(entity, mimeType);
row.addValues(builder);
add(builder.build());
numRows++;
} else {
// Otherwise, say we need to replace a row with this
rowsToReplace.add(row);
}
}
}
// Go through rows needing replacement
for (UntypedRow row: rowsToReplace) {
for (NamedContentValues ncv: oldAccounts) {
ContentValues cv = ncv.values;
// Find a row that hasn't been used (i.e. doesn't match current rows)
if (!cv.containsKey(FOUND_DATA_ROW)) {
// And update it
RowBuilder builder = new RowBuilder(
ContentProviderOperation
.newUpdate(dataUriFromNamedContentValues(ncv)),
ncv);
row.addValues(builder);
add(builder.build());
}
}
} }
builder.withValue(Im.TYPE, type);
builder.withValue(Im.DATA, account);
add(builder.build());
} }
public void addOrganization(Entity entity, int type, String company, String title, public void addOrganization(Entity entity, int type, String company, String title,
String department, String yomiCompanyName) { String department, String yomiCompanyName) {
SmartBuilder builder = createBuilder(entity, Organization.CONTENT_ITEM_TYPE, type); RowBuilder builder = typedRowBuilder(entity, Organization.CONTENT_ITEM_TYPE, type);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, Organization.COMPANY, company) && if (cv != null && cvCompareString(cv, Organization.COMPANY, company) &&
cvCompareString(cv, Organization.PHONETIC_NAME, yomiCompanyName) && cvCompareString(cv, Organization.PHONETIC_NAME, yomiCompanyName) &&
@ -1216,7 +1334,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
public void addNote(Entity entity, String note) { public void addNote(Entity entity, String note) {
SmartBuilder builder = createBuilder(entity, Note.CONTENT_ITEM_TYPE, -1); RowBuilder builder = typedRowBuilder(entity, Note.CONTENT_ITEM_TYPE, -1);
ContentValues cv = builder.cv; ContentValues cv = builder.cv;
if (note != null) { if (note != null) {
note = note.replaceAll("\r\n", "\n"); note = note.replaceAll("\r\n", "\n");
@ -1267,50 +1385,27 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
return "Contacts"; return "Contacts";
} }
private void sendEmail(Serializer s, ContentValues cv) throws IOException { private void sendEmail(Serializer s, ContentValues cv, int count) throws IOException {
// Get both parts of the email address (a newly created one in the UI won't have a name) // Get both parts of the email address (a newly created one in the UI won't have a name)
String addr = cv.getAsString(Email.DATA); String addr = cv.getAsString(Email.DATA);
String name = cv.getAsString(Email.DISPLAY_NAME); String name = cv.getAsString(Email.DISPLAY_NAME);
// Don't crash if we don't have a name
if (name == null) { if (name == null) {
name = ""; name = addr;
} }
String value = null; // Compose address from name and addr
// If there's no addr, just send an empty address (will delete it on the server)
// Otherwise compose it from name and addr
if (addr != null) { if (addr != null) {
value = '\"' + name + "\" <" + addr + '>'; String value = '\"' + name + "\" <" + addr + '>';
} if (count < MAX_EMAIL_ROWS) {
switch (cv.getAsInteger(Email.TYPE)) { s.data(EMAIL_TAGS[count], value);
case TYPE_EMAIL1: }
s.data(Tags.CONTACTS_EMAIL1_ADDRESS, value);
break;
case TYPE_EMAIL2:
s.data(Tags.CONTACTS_EMAIL2_ADDRESS, value);
break;
case TYPE_EMAIL3:
s.data(Tags.CONTACTS_EMAIL3_ADDRESS, value);
break;
default:
break;
} }
} }
private void sendIm(Serializer s, ContentValues cv) throws IOException { private void sendIm(Serializer s, ContentValues cv, int count) throws IOException {
String value = cv.getAsString(Im.DATA); String value = cv.getAsString(Im.DATA);
if (value == null) return; if (value == null) return;
switch (cv.getAsInteger(Im.TYPE)) { if (count < MAX_IM_ROWS) {
case TYPE_IM1: s.data(IM_TAGS[count], value);
s.data(Tags.CONTACTS2_IM_ADDRESS, value);
break;
case TYPE_IM2:
s.data(Tags.CONTACTS2_IM_ADDRESS_2, value);
break;
case TYPE_IM3:
s.data(Tags.CONTACTS2_IM_ADDRESS_3, value);
break;
default:
break;
} }
} }
@ -1473,7 +1568,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
case Phone.TYPE_FAX_WORK: case Phone.TYPE_FAX_WORK:
s.data(Tags.CONTACTS_BUSINESS_FAX_NUMBER, value); s.data(Tags.CONTACTS_BUSINESS_FAX_NUMBER, value);
break; break;
case TYPE_COMPANY_MAIN: case Phone.TYPE_COMPANY_MAIN:
s.data(Tags.CONTACTS2_COMPANY_MAIN_PHONE, value); s.data(Tags.CONTACTS2_COMPANY_MAIN_PHONE, value);
break; break;
case Phone.TYPE_HOME: case Phone.TYPE_HOME:
@ -1485,24 +1580,18 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
case Phone.TYPE_MOBILE: case Phone.TYPE_MOBILE:
s.data(Tags.CONTACTS_MOBILE_TELEPHONE_NUMBER, value); s.data(Tags.CONTACTS_MOBILE_TELEPHONE_NUMBER, value);
break; break;
case TYPE_CAR: case Phone.TYPE_CAR:
s.data(Tags.CONTACTS_CAR_TELEPHONE_NUMBER, value); s.data(Tags.CONTACTS_CAR_TELEPHONE_NUMBER, value);
break; break;
case Phone.TYPE_PAGER: case Phone.TYPE_PAGER:
s.data(Tags.CONTACTS_PAGER_NUMBER, value); s.data(Tags.CONTACTS_PAGER_NUMBER, value);
break; break;
case TYPE_RADIO: case Phone.TYPE_RADIO:
s.data(Tags.CONTACTS_RADIO_TELEPHONE_NUMBER, value); s.data(Tags.CONTACTS_RADIO_TELEPHONE_NUMBER, value);
break; break;
case Phone.TYPE_FAX_HOME: case Phone.TYPE_FAX_HOME:
s.data(Tags.CONTACTS_HOME_FAX_NUMBER, value); s.data(Tags.CONTACTS_HOME_FAX_NUMBER, value);
break; break;
case TYPE_EMAIL2:
s.data(Tags.CONTACTS_EMAIL2_ADDRESS, value);
break;
case TYPE_EMAIL3:
s.data(Tags.CONTACTS_EMAIL3_ADDRESS, value);
break;
default: default:
break; break;
} }
@ -1583,11 +1672,13 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} }
s.start(Tags.SYNC_APPLICATION_DATA); s.start(Tags.SYNC_APPLICATION_DATA);
// Write out the data here // Write out the data here
int imCount = 0;
int emailCount = 0;
for (NamedContentValues ncv: entity.getSubValues()) { for (NamedContentValues ncv: entity.getSubValues()) {
ContentValues cv = ncv.values; ContentValues cv = ncv.values;
String mimeType = cv.getAsString(Data.MIMETYPE); String mimeType = cv.getAsString(Data.MIMETYPE);
if (mimeType.equals(Email.CONTENT_ITEM_TYPE)) { if (mimeType.equals(Email.CONTENT_ITEM_TYPE)) {
sendEmail(s, cv); sendEmail(s, cv, emailCount++);
} else if (mimeType.equals(Nickname.CONTENT_ITEM_TYPE)) { } else if (mimeType.equals(Nickname.CONTENT_ITEM_TYPE)) {
sendNickname(s, cv); sendNickname(s, cv);
} else if (mimeType.equals(EasChildren.CONTENT_ITEM_TYPE)) { } else if (mimeType.equals(EasChildren.CONTENT_ITEM_TYPE)) {
@ -1609,7 +1700,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
} else if (mimeType.equals(Organization.CONTENT_ITEM_TYPE)) { } else if (mimeType.equals(Organization.CONTENT_ITEM_TYPE)) {
sendOrganization(s, cv); sendOrganization(s, cv);
} else if (mimeType.equals(Im.CONTENT_ITEM_TYPE)) { } else if (mimeType.equals(Im.CONTENT_ITEM_TYPE)) {
sendIm(s, cv); sendIm(s, cv, imCount++);
} else if (mimeType.equals(GroupMembership.CONTENT_ITEM_TYPE)) { } else if (mimeType.equals(GroupMembership.CONTENT_ITEM_TYPE)) {
// We must gather these, and send them together (below) // We must gather these, and send them together (below)
groupIds.add(cv.getAsInteger(GroupMembership.GROUP_ROW_ID)); groupIds.add(cv.getAsInteger(GroupMembership.GROUP_ROW_ID));