1d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank/*
2d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Copyright (C) 2008-2009 Marc Blank
3d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Licensed to The Android Open Source Project.
4d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *
5d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Licensed under the Apache License, Version 2.0 (the "License");
6d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * you may not use this file except in compliance with the License.
7d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * You may obtain a copy of the License at
8d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *
9d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *      http://www.apache.org/licenses/LICENSE-2.0
10d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *
11d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Unless required by applicable law or agreed to in writing, software
12d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * distributed under the License is distributed on an "AS IS" BASIS,
13d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * See the License for the specific language governing permissions and
15d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * limitations under the License.
16d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */
17d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
18d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankpackage com.android.exchange.adapter;
19d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
20d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentProviderClient;
21d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentProviderOperation;
22d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentProviderOperation.Builder;
23d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentProviderResult;
24d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentResolver;
25d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentUris;
26d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentValues;
27d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.Entity;
28d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.Entity.NamedContentValues;
29d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.EntityIterator;
30d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.OperationApplicationException;
31d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.database.Cursor;
32d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.net.Uri;
33d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.os.RemoteException;
34d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract;
35d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Email;
36d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Event;
37d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
38d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Im;
39d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Nickname;
40d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Note;
41d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Organization;
42d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Phone;
43d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Photo;
44d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Relation;
45d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
46d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
47d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.CommonDataKinds.Website;
48d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.Data;
49d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.Groups;
50d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.RawContacts;
51d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.RawContactsEntity;
52d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.Settings;
53d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.ContactsContract.SyncState;
54d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.provider.SyncStateContract;
55d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.text.TextUtils;
56d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.text.util.Rfc822Token;
57d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.text.util.Rfc822Tokenizer;
58d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.util.Base64;
59d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.util.Log;
60d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
610969df734735b0b55cc6a800335f665b40c8ecd6Marc Blankimport com.android.emailcommon.utility.Utility;
62d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.CommandStatusException;
63d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.Eas;
64d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.EasSyncService;
650969df734735b0b55cc6a800335f665b40c8ecd6Marc Blankimport com.android.exchange.utility.CalendarUtilities;
66d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
67d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.io.IOException;
68d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.io.InputStream;
69d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.util.ArrayList;
700969df734735b0b55cc6a800335f665b40c8ecd6Marc Blankimport java.util.GregorianCalendar;
710969df734735b0b55cc6a800335f665b40c8ecd6Marc Blankimport java.util.TimeZone;
72d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
73d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank/**
74d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Sync adapter for EAS Contacts
75d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *
76d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */
77d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankpublic class ContactsSyncAdapter extends AbstractSyncAdapter {
78d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
79d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String TAG = "EasContactsSyncAdapter";
80d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String SERVER_ID_SELECTION = RawContacts.SOURCE_ID + "=?";
81d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String CLIENT_ID_SELECTION = RawContacts.SYNC1 + "=?";
82d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String[] ID_PROJECTION = new String[] {RawContacts._ID};
83d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String[] GROUP_TITLE_PROJECTION = new String[] {Groups.TITLE};
84d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String MIMETYPE_GROUP_MEMBERSHIP_AND_ID_EQUALS = Data.MIMETYPE + "='" +
85d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        GroupMembership.CONTENT_ITEM_TYPE + "' AND " + GroupMembership.GROUP_ROW_ID + "=?";
86d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String[] GROUPS_ID_PROJECTION = new String[] {Groups._ID};
87d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
88d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final ArrayList<NamedContentValues> EMPTY_ARRAY_NAMEDCONTENTVALUES
89d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        = new ArrayList<NamedContentValues>();
90d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
91d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String FOUND_DATA_ROW = "com.android.exchange.FOUND_ROW";
92d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
93d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int[] HOME_ADDRESS_TAGS = new int[] {Tags.CONTACTS_HOME_ADDRESS_CITY,
94d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_HOME_ADDRESS_COUNTRY,
95d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_HOME_ADDRESS_POSTAL_CODE,
96d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_HOME_ADDRESS_STATE,
97d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_HOME_ADDRESS_STREET};
98d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
99d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int[] WORK_ADDRESS_TAGS = new int[] {Tags.CONTACTS_BUSINESS_ADDRESS_CITY,
100d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_BUSINESS_ADDRESS_COUNTRY,
101d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_BUSINESS_ADDRESS_POSTAL_CODE,
102d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_BUSINESS_ADDRESS_STATE,
103d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_BUSINESS_ADDRESS_STREET};
104d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
105d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int[] OTHER_ADDRESS_TAGS = new int[] {Tags.CONTACTS_HOME_ADDRESS_CITY,
106d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_OTHER_ADDRESS_COUNTRY,
107d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_OTHER_ADDRESS_POSTAL_CODE,
108d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_OTHER_ADDRESS_STATE,
109d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_OTHER_ADDRESS_STREET};
110d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
111d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int MAX_IM_ROWS = 3;
112d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int MAX_EMAIL_ROWS = 3;
113d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int MAX_PHONE_ROWS = 2;
114d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String COMMON_DATA_ROW = Im.DATA;  // Could have been Email.DATA, etc.
115d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final String COMMON_TYPE_ROW = Phone.TYPE; // Could have been any typed row
116d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
117d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int[] IM_TAGS = new int[] {Tags.CONTACTS2_IM_ADDRESS,
118d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS2_IM_ADDRESS_2, Tags.CONTACTS2_IM_ADDRESS_3};
119d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
120d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int[] EMAIL_TAGS = new int[] {Tags.CONTACTS_EMAIL1_ADDRESS,
121d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_EMAIL2_ADDRESS, Tags.CONTACTS_EMAIL3_ADDRESS};
122d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
123d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int[] WORK_PHONE_TAGS = new int[] {Tags.CONTACTS_BUSINESS_TELEPHONE_NUMBER,
124d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER};
125d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
126d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int[] HOME_PHONE_TAGS = new int[] {Tags.CONTACTS_HOME_TELEPHONE_NUMBER,
127d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Tags.CONTACTS_HOME2_TELEPHONE_NUMBER};
128d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
129d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final Object sSyncKeyLock = new Object();
130d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
131d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
132d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
133d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
134d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private final Uri mAccountUri;
135d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private final ContentResolver mContentResolver;
136d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private boolean mGroupsUsed = false;
137d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
138d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public ContactsSyncAdapter(EasSyncService service) {
139d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        super(service);
140d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        mAccountUri = uriWithAccountAndIsSyncAdapter(RawContacts.CONTENT_URI);
141d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        mContentResolver = mContext.getContentResolver();
142d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
143d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
144d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    static Uri addCallerIsSyncAdapterParameter(Uri uri) {
145d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return uri.buildUpon()
146d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
147d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                .build();
148d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
149d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
150d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
1510969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank    public void sendSyncOptions(Double protocolVersion, Serializer s, boolean initialSync)
1520969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            throws IOException  {
1530969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        if (initialSync) {
1540969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            // These are the tags we support for upload; whenever we add/remove support
1550969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            // (in addData), we need to update this list
1560969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.start(Tags.SYNC_SUPPORTED);
1570969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_FIRST_NAME);
1580969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_LAST_NAME);
1590969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_MIDDLE_NAME);
1600969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_SUFFIX);
1610969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_COMPANY_NAME);
1620969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_JOB_TITLE);
1630969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_EMAIL1_ADDRESS);
1640969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_EMAIL2_ADDRESS);
1650969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_EMAIL3_ADDRESS);
1660969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER);
1670969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS_TELEPHONE_NUMBER);
1680969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_MMS);
1690969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS_FAX_NUMBER);
1700969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_COMPANY_MAIN_PHONE);
1710969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME_FAX_NUMBER);
1720969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME_TELEPHONE_NUMBER);
1730969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME2_TELEPHONE_NUMBER);
1740969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_MOBILE_TELEPHONE_NUMBER);
1750969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_CAR_TELEPHONE_NUMBER);
1760969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_RADIO_TELEPHONE_NUMBER);
1770969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_PAGER_NUMBER);
1780969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER);
1790969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_IM_ADDRESS);
1800969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_IM_ADDRESS_2);
1810969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_IM_ADDRESS_3);
1820969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS_ADDRESS_CITY);
1830969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS_ADDRESS_COUNTRY);
1840969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS_ADDRESS_POSTAL_CODE);
1850969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS_ADDRESS_STATE);
1860969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BUSINESS_ADDRESS_STREET);
1870969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME_ADDRESS_CITY);
1880969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME_ADDRESS_COUNTRY);
1890969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME_ADDRESS_POSTAL_CODE);
1900969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME_ADDRESS_STATE);
1910969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_HOME_ADDRESS_STREET);
1920969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_OTHER_ADDRESS_CITY);
1930969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_OTHER_ADDRESS_COUNTRY);
1940969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_OTHER_ADDRESS_POSTAL_CODE);
1950969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_OTHER_ADDRESS_STATE);
1960969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_OTHER_ADDRESS_STREET);
1970969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_YOMI_COMPANY_NAME);
1980969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_YOMI_FIRST_NAME);
1990969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_YOMI_LAST_NAME);
2000969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_NICKNAME);
2010969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_ASSISTANT_NAME);
2020969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_MANAGER_NAME);
2030969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_SPOUSE);
2040969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_DEPARTMENT);
2050969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_TITLE);
2060969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_OFFICE_LOCATION);
2070969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_CUSTOMER_ID);
2080969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_GOVERNMENT_ID);
2090969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS2_ACCOUNT_NAME);
2100969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_ANNIVERSARY);
2110969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_BIRTHDAY);
2120969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_WEBPAGE);
2130969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.tag(Tags.CONTACTS_PICTURE);
2140969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            s.end(); // SYNC_SUPPORTED
2150969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        } else {
2160969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            setPimSyncOptions(protocolVersion, null, s);
2170969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        }
218d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
219d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
220d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
221d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public boolean isSyncable() {
222d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return ContentResolver.getSyncAutomatically(
223d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                mAccountManagerAccount, ContactsContract.AUTHORITY);
224d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
225d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
226d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
227d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public boolean parse(InputStream is) throws IOException, CommandStatusException {
228d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        EasContactsSyncParser p = new EasContactsSyncParser(is, this);
229d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return p.parse();
230d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
231d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
232d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
233d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
234d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public void wipe() {
235d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        mContentResolver.delete(mAccountUri, null, null);
236d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
237d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
238d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    interface UntypedRow {
239d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addValues(RowBuilder builder);
240d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public boolean isSameAs(int type, String value);
241d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
242d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
243d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    /**
244d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * We get our SyncKey from ContactsProvider.  If there's not one, we set it to "0" (the reset
245d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * state) and save that away.
246d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     */
247d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
248d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public String getSyncKey() throws IOException {
249d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        synchronized (sSyncKeyLock) {
250d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentProviderClient client = mService.mContentResolver
251d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
252d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            try {
253d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                byte[] data = SyncStateContract.Helpers.get(client,
254d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ContactsContract.SyncState.CONTENT_URI, mAccountManagerAccount);
255d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (data == null || data.length == 0) {
256d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // Initialize the SyncKey
257d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    setSyncKey("0", false);
258d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // Make sure ungrouped contacts for Exchange are defaultly visible
259d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    ContentValues cv = new ContentValues();
260d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cv.put(Groups.ACCOUNT_NAME, mAccount.mEmailAddress);
261d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cv.put(Groups.ACCOUNT_TYPE, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
262d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cv.put(Settings.UNGROUPED_VISIBLE, true);
263d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    client.insert(addCallerIsSyncAdapterParameter(Settings.CONTENT_URI), cv);
264d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    return "0";
265d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
266d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    return new String(data);
267d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
268d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } catch (RemoteException e) {
269d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                throw new IOException("Can't get SyncKey from ContactsProvider");
270d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
271d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
272d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
273d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
274d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    /**
275d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * We only need to set this when we're forced to make the SyncKey "0" (a reset).  In all other
276d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * cases, the SyncKey is set within ContactOperations
277d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     */
278d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
279d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public void setSyncKey(String syncKey, boolean inCommands) throws IOException {
280d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        synchronized (sSyncKeyLock) {
281d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if ("0".equals(syncKey) || !inCommands) {
282d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ContentProviderClient client = mService.mContentResolver
283d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
284d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                try {
285d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    SyncStateContract.Helpers.set(client, ContactsContract.SyncState.CONTENT_URI,
286d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            mAccountManagerAccount, syncKey.getBytes());
287d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    userLog("SyncKey set to ", syncKey, " in ContactsProvider");
288d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } catch (RemoteException e) {
289d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    throw new IOException("Can't set SyncKey in ContactsProvider");
290d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
291d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
292d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mMailbox.mSyncKey = syncKey;
293d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
294d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
295d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
296d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public static final class EasChildren {
297d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private EasChildren() {}
298d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
299d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        /** MIME type used when storing this in data table. */
300d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/eas_children";
301d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final int MAX_CHILDREN = 8;
302d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String[] ROWS =
303d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            new String[] {"data2", "data3", "data4", "data5", "data6", "data7", "data8", "data9"};
304d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
305d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
306d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public static final class EasPersonal {
307d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String anniversary;
308d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String fileAs;
309d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
310d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            /** MIME type used when storing this in data table. */
311d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/eas_personal";
312d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String ANNIVERSARY = "data2";
313d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String FILE_AS = "data4";
314d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
315d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        boolean hasData() {
316d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return anniversary != null || fileAs != null;
317d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
318d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
319d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
320d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public static final class EasBusiness {
321d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String customerId;
322d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String governmentId;
323d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String accountName;
324d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
325d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        /** MIME type used when storing this in data table. */
326d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/eas_business";
327d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String CUSTOMER_ID = "data6";
328d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String GOVERNMENT_ID = "data7";
329d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public static final String ACCOUNT_NAME = "data8";
330d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
331d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        boolean hasData() {
332d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return customerId != null || governmentId != null || accountName != null;
333d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
334d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
335d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
336d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public static final class Address {
337d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String city;
338d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String country;
339d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String code;
340d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String street;
341d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String state;
342d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
343d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        boolean hasData() {
344d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return city != null || country != null || code != null || state != null
345d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                || street != null;
346d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
347d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
348d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
349d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    class EmailRow implements UntypedRow {
350d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String email;
351d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String displayName;
352d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
353d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public EmailRow(String _email) {
354d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(_email);
355d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Can't happen, but belt & suspenders
356d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (tokens.length == 0) {
357d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                email = "";
358d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                displayName = "";
359d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } else {
360d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                Rfc822Token token = tokens[0];
361d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                email = token.getAddress();
362d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                displayName = token.getName();
363d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
364d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
365d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
3660969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        @Override
367d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addValues(RowBuilder builder) {
368d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Email.DATA, email);
369d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Email.DISPLAY_NAME, displayName);
370d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
371d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
3720969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        @Override
373d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public boolean isSameAs(int type, String value) {
374d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return email.equalsIgnoreCase(value);
375d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
376d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
377d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
378d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    class ImRow implements UntypedRow {
379d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String im;
380d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
381d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public ImRow(String _im) {
382d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            im = _im;
383d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
384d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
3850969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        @Override
386d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addValues(RowBuilder builder) {
387d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Im.DATA, im);
388d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
389d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
3900969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        @Override
391d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public boolean isSameAs(int type, String value) {
392d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return im.equalsIgnoreCase(value);
393d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
394d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
395d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
396d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    class PhoneRow implements UntypedRow {
397d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String phone;
398d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        int type;
399d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
400d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public PhoneRow(String _phone, int _type) {
401d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            phone = _phone;
402d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            type = _type;
403d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
404d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
4050969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        @Override
406d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addValues(RowBuilder builder) {
407d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Im.DATA, phone);
408d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Phone.TYPE, type);
409d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
410d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
4110969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank        @Override
412d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public boolean isSameAs(int _type, String value) {
413d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return type == _type && phone.equalsIgnoreCase(value);
414d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
415d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
416d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
417d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank   class EasContactsSyncParser extends AbstractSyncParser {
418d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
419d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String[] mBindArgument = new String[1];
420d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String mMailboxIdAsString;
421d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContactOperations ops = new ContactOperations();
422d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
423d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public EasContactsSyncParser(InputStream in, ContactsSyncAdapter adapter)
424d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                throws IOException {
425d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            super(in, adapter);
426d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
427d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
428d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addData(String serverId, ContactOperations ops, Entity entity)
429d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                throws IOException {
430d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String prefix = null;
431d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String firstName = null;
432d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String lastName = null;
433d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String middleName = null;
434d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String suffix = null;
435d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String companyName = null;
436d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String yomiFirstName = null;
437d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String yomiLastName = null;
438d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String yomiCompanyName = null;
439d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String title = null;
440d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String department = null;
441d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String officeLocation = null;
442d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Address home = new Address();
443d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Address work = new Address();
444d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Address other = new Address();
445d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            EasBusiness business = new EasBusiness();
446d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            EasPersonal personal = new EasPersonal();
447d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<String> children = new ArrayList<String>();
448d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<UntypedRow> emails = new ArrayList<UntypedRow>();
449d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<UntypedRow> ims = new ArrayList<UntypedRow>();
450d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<UntypedRow> homePhones = new ArrayList<UntypedRow>();
451d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<UntypedRow> workPhones = new ArrayList<UntypedRow>();
452d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (entity == null) {
453d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ops.newContact(serverId);
454d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
455d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
456d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) {
457d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
458d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_FIRST_NAME:
459d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        firstName = getValue();
460d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
461d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_LAST_NAME:
462d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        lastName = getValue();
463d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
464d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_MIDDLE_NAME:
465d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        middleName = getValue();
466d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
467d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_SUFFIX:
468d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        suffix = getValue();
469d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
470d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_COMPANY_NAME:
471d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        companyName = getValue();
472d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
473d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_JOB_TITLE:
474d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        title = getValue();
475d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
476d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_EMAIL1_ADDRESS:
477d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_EMAIL2_ADDRESS:
478d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_EMAIL3_ADDRESS:
479d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        emails.add(new EmailRow(getValue()));
480d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
481d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS2_TELEPHONE_NUMBER:
482d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS_TELEPHONE_NUMBER:
483d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        workPhones.add(new PhoneRow(getValue(), Phone.TYPE_WORK));
484d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
485d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_MMS:
486d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_MMS, getValue());
487d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
488d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS_FAX_NUMBER:
489d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_FAX_WORK, getValue());
490d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
491d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_COMPANY_MAIN_PHONE:
492d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_COMPANY_MAIN, getValue());
493d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
494d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME_FAX_NUMBER:
495d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_FAX_HOME, getValue());
496d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
497d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME_TELEPHONE_NUMBER:
498d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME2_TELEPHONE_NUMBER:
499d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        homePhones.add(new PhoneRow(getValue(), Phone.TYPE_HOME));
500d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
501d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_MOBILE_TELEPHONE_NUMBER:
502d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_MOBILE, getValue());
503d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
504d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_CAR_TELEPHONE_NUMBER:
505d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_CAR, getValue());
506d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
507d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_RADIO_TELEPHONE_NUMBER:
508d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_RADIO, getValue());
509d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
510d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_PAGER_NUMBER:
511d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_PAGER, getValue());
512d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
513d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER:
514d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhone(entity, Phone.TYPE_ASSISTANT, getValue());
515d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
516d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_IM_ADDRESS:
517d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_IM_ADDRESS_2:
518d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_IM_ADDRESS_3:
519d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ims.add(new ImRow(getValue()));
520d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
521d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS_ADDRESS_CITY:
522d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        work.city = getValue();
523d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
524d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS_ADDRESS_COUNTRY:
525d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        work.country = getValue();
526d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
527d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS_ADDRESS_POSTAL_CODE:
528d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        work.code = getValue();
529d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
530d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS_ADDRESS_STATE:
531d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        work.state = getValue();
532d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
533d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BUSINESS_ADDRESS_STREET:
534d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        work.street = getValue();
535d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
536d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME_ADDRESS_CITY:
537d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        home.city = getValue();
538d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
539d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME_ADDRESS_COUNTRY:
540d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        home.country = getValue();
541d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
542d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME_ADDRESS_POSTAL_CODE:
543d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        home.code = getValue();
544d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
545d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME_ADDRESS_STATE:
546d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        home.state = getValue();
547d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
548d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_HOME_ADDRESS_STREET:
549d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        home.street = getValue();
550d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
551d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_OTHER_ADDRESS_CITY:
552d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        other.city = getValue();
553d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
554d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_OTHER_ADDRESS_COUNTRY:
555d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        other.country = getValue();
556d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
557d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_OTHER_ADDRESS_POSTAL_CODE:
558d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        other.code = getValue();
559d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
560d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_OTHER_ADDRESS_STATE:
561d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        other.state = getValue();
562d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
563d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_OTHER_ADDRESS_STREET:
564d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        other.street = getValue();
565d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
566d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
567d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_CHILDREN:
568d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        childrenParser(children);
569d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
570d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
571d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_YOMI_COMPANY_NAME:
572d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        yomiCompanyName = getValue();
573d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
574d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_YOMI_FIRST_NAME:
575d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        yomiFirstName = getValue();
576d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
577d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_YOMI_LAST_NAME:
578d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        yomiLastName = getValue();
579d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
580d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
581d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_NICKNAME:
582d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addNickname(entity, getValue());
583d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
584d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
585d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_ASSISTANT_NAME:
586d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addRelation(entity, Relation.TYPE_ASSISTANT, getValue());
587d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
588d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_MANAGER_NAME:
589d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addRelation(entity, Relation.TYPE_MANAGER, getValue());
590d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
591d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_SPOUSE:
592d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addRelation(entity, Relation.TYPE_SPOUSE, getValue());
593d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
594d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_DEPARTMENT:
595d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        department = getValue();
596d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
597d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_TITLE:
598d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        prefix = getValue();
599d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
600d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
601d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // EAS Business
602d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_OFFICE_LOCATION:
603d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        officeLocation = getValue();
604d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
605d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_CUSTOMER_ID:
606d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        business.customerId = getValue();
607d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
608d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_GOVERNMENT_ID:
609d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        business.governmentId = getValue();
610d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
611d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS2_ACCOUNT_NAME:
612d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        business.accountName = getValue();
613d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
614d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
615d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // EAS Personal
616d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_ANNIVERSARY:
617d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        personal.anniversary = getValue();
618d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
619d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BIRTHDAY:
620d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addBirthday(entity, getValue());
621d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
622d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_WEBPAGE:
623d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addWebpage(entity, getValue());
624d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
625d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
626d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_PICTURE:
627d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addPhoto(entity, getValue());
628d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
629d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
630d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.BASE_BODY:
631d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addNote(entity, bodyParser());
632d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
633d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_BODY:
634d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addNote(entity, getValue());
635d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
636d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
637d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_CATEGORIES:
638d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        mGroupsUsed = true;
639d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        categoriesParser(ops, entity);
640d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
641d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
642d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
643d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
644d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
645d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
646d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
647d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // We must have first name, last name, or company name
648d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String name = null;
649d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (firstName != null || lastName != null) {
650d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (firstName == null) {
651d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    name = lastName;
652d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (lastName == null) {
653d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    name = firstName;
654d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
655d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    name = firstName + ' ' + lastName;
656d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
657d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } else if (companyName != null) {
658d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                name = companyName;
659d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
660d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
661d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.addName(entity, prefix, firstName, lastName, middleName, suffix, name,
6620969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank                    yomiFirstName, yomiLastName);
663d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.addBusiness(entity, business);
664d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.addPersonal(entity, personal);
665d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
666d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.addUntyped(entity, emails, Email.CONTENT_ITEM_TYPE, -1, MAX_EMAIL_ROWS);
667d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.addUntyped(entity, ims, Im.CONTENT_ITEM_TYPE, -1, MAX_IM_ROWS);
668d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.addUntyped(entity, homePhones, Phone.CONTENT_ITEM_TYPE, Phone.TYPE_HOME,
669d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    MAX_PHONE_ROWS);
670d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.addUntyped(entity, workPhones, Phone.CONTENT_ITEM_TYPE, Phone.TYPE_WORK,
671d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    MAX_PHONE_ROWS);
672d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
673d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (!children.isEmpty()) {
674d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ops.addChildren(entity, children);
675d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
676d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
677d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (work.hasData()) {
678d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ops.addPostal(entity, StructuredPostal.TYPE_WORK, work.street, work.city,
679d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        work.state, work.country, work.code);
680d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
681d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (home.hasData()) {
682d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ops.addPostal(entity, StructuredPostal.TYPE_HOME, home.street, home.city,
683d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        home.state, home.country, home.code);
684d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
685d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (other.hasData()) {
686d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ops.addPostal(entity, StructuredPostal.TYPE_OTHER, other.street, other.city,
687d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        other.state, other.country, other.code);
688d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
689d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
690d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (companyName != null) {
691d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ops.addOrganization(entity, Organization.TYPE_WORK, companyName, title, department,
692d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        yomiCompanyName, officeLocation);
693d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
694d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
695d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (entity != null) {
696d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // We've been removing rows from the list as they've been found in the xml
697d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // Any that are left must have been deleted on the server
698d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ArrayList<NamedContentValues> ncvList = entity.getSubValues();
699d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                for (NamedContentValues ncv: ncvList) {
700d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // These rows need to be deleted...
701d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    Uri u = dataUriFromNamedContentValues(ncv);
702d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    ops.add(ContentProviderOperation.newDelete(addCallerIsSyncAdapterParameter(u))
703d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .build());
704d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
705d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
706d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
707d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
708d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private void categoriesParser(ContactOperations ops, Entity entity) throws IOException {
709d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.CONTACTS_CATEGORIES) != END) {
710d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
711d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_CATEGORY:
712d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        ops.addGroup(entity, getValue());
713d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
714d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
715d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
716d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
717d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
718d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
719d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
720d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private void childrenParser(ArrayList<String> children) throws IOException {
721d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.CONTACTS_CHILDREN) != END) {
722d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
723d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.CONTACTS_CHILD:
724d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        if (children.size() < EasChildren.MAX_CHILDREN) {
725d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            children.add(getValue());
726d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
727d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
728d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
729d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
730d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
731d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
732d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
733d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
734d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private String bodyParser() throws IOException {
735d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String body = null;
736d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.BASE_BODY) != END) {
737d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
738d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.BASE_DATA:
739d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        body = getValue();
740d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
741d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
742d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
743d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
744d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
745d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return body;
746d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
747d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
748d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addParser(ContactOperations ops) throws IOException {
749d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String serverId = null;
750d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_ADD) != END) {
751d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
752d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_SERVER_ID: // same as
753d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        serverId = getValue();
754d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
755d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_APPLICATION_DATA:
756d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        addData(serverId, ops, null);
757d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
758d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
759d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
760d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
761d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
762d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
763d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
764d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private Cursor getServerIdCursor(String serverId) {
765d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mBindArgument[0] = serverId;
766d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return mContentResolver.query(mAccountUri, ID_PROJECTION, SERVER_ID_SELECTION,
767d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    mBindArgument, null);
768d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
769d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
770d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private Cursor getClientIdCursor(String clientId) {
771d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mBindArgument[0] = clientId;
772d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return mContentResolver.query(mAccountUri, ID_PROJECTION, CLIENT_ID_SELECTION,
773d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    mBindArgument, null);
774d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
775d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
776d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void deleteParser(ContactOperations ops) throws IOException {
777d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_DELETE) != END) {
778d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
779d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_SERVER_ID:
780d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        String serverId = getValue();
781d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // Find the message in this mailbox with the given serverId
782d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        Cursor c = getServerIdCursor(serverId);
783d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        try {
784d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            if (c.moveToFirst()) {
785d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                userLog("Deleting ", serverId);
786d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                ops.delete(c.getLong(0));
787d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            }
788d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        } finally {
789d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            c.close();
790d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
791d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
792d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
793d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
794d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
795d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
796d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
797d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
798d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        class ServerChange {
799d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            long id;
800d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            boolean read;
801d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
802d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ServerChange(long _id, boolean _read) {
803d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                id = _id;
804d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                read = _read;
805d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
806d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
807d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
808d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        /**
809d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * Changes are handled row by row, and only changed/new rows are acted upon
810d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param ops the array of pending ContactProviderOperations.
811d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @throws IOException
812d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         */
813d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void changeParser(ContactOperations ops) throws IOException {
814d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String serverId = null;
815d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Entity entity = null;
816d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_CHANGE) != END) {
817d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
818d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_SERVER_ID:
819d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        serverId = getValue();
820d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        Cursor c = getServerIdCursor(serverId);
821d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        try {
822d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            if (c.moveToFirst()) {
823d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                // TODO Handle deleted individual rows...
824d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                Uri uri = ContentUris.withAppendedId(
825d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                        RawContacts.CONTENT_URI, c.getLong(0));
826d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                uri = Uri.withAppendedPath(
827d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                        uri, RawContacts.Entity.CONTENT_DIRECTORY);
828d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                EntityIterator entityIterator = RawContacts.newEntityIterator(
829d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    mContentResolver.query(uri, null, null, null, null));
830d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                if (entityIterator.hasNext()) {
831d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    entity = entityIterator.next();
832d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                }
833d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                userLog("Changing contact ", serverId);
834d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            }
835d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        } finally {
836d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            c.close();
837d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
838d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
839d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_APPLICATION_DATA:
840d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        addData(serverId, ops, entity);
841d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
842d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
843d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
844d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
845d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
846d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
847d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
848d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        @Override
849d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void commandsParser() throws IOException {
850d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_COMMANDS) != END) {
851d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (tag == Tags.SYNC_ADD) {
852d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    addParser(ops);
853d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    incrementChangeCount();
854d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SYNC_DELETE) {
855d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    deleteParser(ops);
856d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    incrementChangeCount();
857d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SYNC_CHANGE) {
858d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    changeParser(ops);
859d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    incrementChangeCount();
860d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else
861d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    skipTag();
862d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
863d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
864d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
865d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        @Override
866d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void commit() throws IOException {
867d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank           // Save the syncKey here, using the Helper provider by Contacts provider
868d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            userLog("Contacts SyncKey saved as: ", mMailbox.mSyncKey);
869d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.add(SyncStateContract.Helpers.newSetOperation(SyncState.CONTENT_URI,
870d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    mAccountManagerAccount, mMailbox.mSyncKey.getBytes()));
871d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
872d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Execute these all at once...
873d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.execute();
874d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
875d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (ops.mResults != null) {
876d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ContentValues cv = new ContentValues();
877d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                cv.put(RawContacts.DIRTY, 0);
878d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                for (int i = 0; i < ops.mContactIndexCount; i++) {
879d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    int index = ops.mContactIndexArray[i];
880d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    Uri u = ops.mResults[index].uri;
881d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (u != null) {
882d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        String idString = u.getLastPathSegment();
883d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        mContentResolver.update(
884d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                addCallerIsSyncAdapterParameter(RawContacts.CONTENT_URI), cv,
885d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                RawContacts._ID + "=" + idString, null);
886d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
887d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
888d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
889d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
890d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
891d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addResponsesParser() throws IOException {
892d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String serverId = null;
893d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String clientId = null;
894d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = new ContentValues();
895d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_ADD) != END) {
896d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
897d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_SERVER_ID:
898d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        serverId = getValue();
899d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
900d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_CLIENT_ID:
901d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        clientId = getValue();
902d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
903d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_STATUS:
904d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        getValue();
905d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
906d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
907d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
908d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
909d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
910d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
911d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // This is theoretically impossible, but...
912d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (clientId == null || serverId == null) return;
913d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
914d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Cursor c = getClientIdCursor(clientId);
915d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            try {
916d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (c.moveToFirst()) {
917d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cv.put(RawContacts.SOURCE_ID, serverId);
918d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cv.put(RawContacts.DIRTY, 0);
919d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    ops.add(ContentProviderOperation.newUpdate(
920d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            ContentUris.withAppendedId(
921d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    addCallerIsSyncAdapterParameter(RawContacts.CONTENT_URI),
922d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    c.getLong(0)))
923d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .withValues(cv)
924d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .build());
925d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    userLog("New contact " + clientId + " was given serverId: " + serverId);
926d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
927d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } finally {
928d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                c.close();
929d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
930d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
931d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
932d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void changeResponsesParser() throws IOException {
933d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String serverId = null;
934d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String status = null;
935d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_CHANGE) != END) {
936d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                switch (tag) {
937d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_SERVER_ID:
938d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        serverId = getValue();
939d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
940d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    case Tags.SYNC_STATUS:
941d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        status = getValue();
942d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
943d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    default:
944d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        skipTag();
945d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
946d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
947d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (serverId != null && status != null) {
948d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                userLog("Changed contact " + serverId + " failed with status: " + status);
949d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
950d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
951d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
952d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
953d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        @Override
954d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void responsesParser() throws IOException {
955d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Handle server responses here (for Add and Change)
956d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SYNC_RESPONSES) != END) {
957d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (tag == Tags.SYNC_ADD) {
958d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    addResponsesParser();
959d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SYNC_CHANGE) {
960d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    changeResponsesParser();
961d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else
962d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    skipTag();
963d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
964d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
965d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
966d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
967d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
968d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private Uri uriWithAccountAndIsSyncAdapter(Uri uri) {
969d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return uri.buildUpon()
970d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccount.mEmailAddress)
971d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            .appendQueryParameter(RawContacts.ACCOUNT_TYPE, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
972d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
973d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            .build();
974d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
975d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
976d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    /**
977d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * SmartBuilder is a wrapper for the Builder class that is used to create/update rows for a
978d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * ContentProvider.  It has, in addition to the Builder, ContentValues which, if present,
979d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * represent the current values of that row, that can be compared against current values to
980d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * see whether an update is even necessary.  The methods on SmartBuilder are delegated to
981d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * the Builder.
982d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     */
983d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private class RowBuilder {
984d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Builder builder;
985d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContentValues cv;
986d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
987d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public RowBuilder(Builder _builder) {
988d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder = _builder;
989d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
990d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
991d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public RowBuilder(Builder _builder, NamedContentValues _ncv) {
992d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder = _builder;
993d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            cv = _ncv.values;
994d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
995d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
996b931f82fa44c2e26e2645c0d5fde9eef3e666efdPaul Westbrook        RowBuilder withValues(ContentValues values) {
997b931f82fa44c2e26e2645c0d5fde9eef3e666efdPaul Westbrook            builder.withValues(values);
998b931f82fa44c2e26e2645c0d5fde9eef3e666efdPaul Westbrook            return this;
999b931f82fa44c2e26e2645c0d5fde9eef3e666efdPaul Westbrook        }
1000b931f82fa44c2e26e2645c0d5fde9eef3e666efdPaul Westbrook
1001d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        RowBuilder withValueBackReference(String key, int previousResult) {
1002d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValueBackReference(key, previousResult);
1003d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return this;
1004d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1005d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1006d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContentProviderOperation build() {
1007d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return builder.build();
1008d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1009d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1010d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        RowBuilder withValue(String key, Object value) {
1011d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(key, value);
1012d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return this;
1013d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1014d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1015d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1016d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private class ContactOperations extends ArrayList<ContentProviderOperation> {
1017d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private static final long serialVersionUID = 1L;
1018d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private int mCount = 0;
1019d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private int mContactBackValue = mCount;
1020d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Make an array big enough for the PIM window (max items we can get)
1021d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private int[] mContactIndexArray =
1022d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            new int[Integer.parseInt(AbstractSyncAdapter.PIM_WINDOW_SIZE)];
1023d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private int mContactIndexCount = 0;
1024d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private ContentProviderResult[] mResults = null;
1025d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1026d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        @Override
1027d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public boolean add(ContentProviderOperation op) {
1028d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            super.add(op);
1029d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mCount++;
1030d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return true;
1031d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1032d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1033d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void newContact(String serverId) {
1034d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Builder builder = ContentProviderOperation
1035d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                .newInsert(uriWithAccountAndIsSyncAdapter(RawContacts.CONTENT_URI));
1036d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues values = new ContentValues();
1037d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            values.put(RawContacts.SOURCE_ID, serverId);
1038d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValues(values);
1039d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mContactBackValue = mCount;
1040d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mContactIndexArray[mContactIndexCount++] = mCount;
1041d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1042d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1043d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1044d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void delete(long id) {
1045d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(ContentProviderOperation
1046d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    .newDelete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)
1047d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .buildUpon()
1048d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
1049d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .build())
1050d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    .build());
1051d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1052d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1053d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void execute() {
1054d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            synchronized (mService.getSynchronizer()) {
1055d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (!mService.isStopped()) {
1056d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    try {
1057d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        if (!isEmpty()) {
1058d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            mService.userLog("Executing ", size(), " CPO's");
1059d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            mResults = mContext.getContentResolver().applyBatch(
1060d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    ContactsContract.AUTHORITY, this);
1061d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
1062d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } catch (RemoteException e) {
1063d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // There is nothing sensible to be done here
1064d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        Log.e(TAG, "problem inserting contact during server update", e);
1065d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } catch (OperationApplicationException e) {
1066d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // There is nothing sensible to be done here
1067d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        Log.e(TAG, "problem inserting contact during server update", e);
1068d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1069d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1070d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1071d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1072d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1073d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        /**
1074d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * Given the list of NamedContentValues for an entity, a mime type, and a subtype,
1075d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * tries to find a match, returning it
1076d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param list the list of NCV's from the contact entity
1077d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param contentItemType the mime type we're looking for
1078d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param type the subtype (e.g. HOME, WORK, etc.)
1079d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @return the matching NCV or null if not found
1080d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         */
1081d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private NamedContentValues findTypedData(ArrayList<NamedContentValues> list,
1082d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String contentItemType, int type, String stringType) {
1083d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            NamedContentValues result = null;
1084d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1085d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Loop through the ncv's, looking for an existing row
1086d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            for (NamedContentValues namedContentValues: list) {
1087d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                Uri uri = namedContentValues.uri;
1088d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ContentValues cv = namedContentValues.values;
1089d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (Data.CONTENT_URI.equals(uri)) {
1090d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    String mimeType = cv.getAsString(Data.MIMETYPE);
1091d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (mimeType.equals(contentItemType)) {
1092d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        if (stringType != null) {
1093d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            if (cv.getAsString(GroupMembership.GROUP_ROW_ID).equals(stringType)) {
1094d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                result = namedContentValues;
1095d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            }
1096d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // Note Email.TYPE could be ANY type column; they are all defined in
1097d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // the private CommonColumns class in ContactsContract
1098d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // We'll accept either type < 0 (don't care), cv doesn't have a type,
1099d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // or the types are equal
1100d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        } else if (type < 0 || !cv.containsKey(Email.TYPE) ||
1101d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                cv.getAsInteger(Email.TYPE) == type) {
1102d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            result = namedContentValues;
1103d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
1104d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1105d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1106d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1107d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1108d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // If we've found an existing data row, we'll delete it.  Any rows left at the
1109d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // end should be deleted...
1110d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (result != null) {
1111d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                list.remove(result);
1112d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1113d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1114d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Return the row found (or null)
1115d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return result;
1116d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1117d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1118d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        /**
1119d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * Given the list of NamedContentValues for an entity and a mime type
1120d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * gather all of the matching NCV's, returning them
1121d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param list the list of NCV's from the contact entity
1122d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param contentItemType the mime type we're looking for
1123d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param type the subtype (e.g. HOME, WORK, etc.)
1124d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @return the matching NCVs
1125d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         */
1126d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private ArrayList<NamedContentValues> findUntypedData(ArrayList<NamedContentValues> list,
1127d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                int type, String contentItemType) {
1128d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<NamedContentValues> result = new ArrayList<NamedContentValues>();
1129d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1130d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Loop through the ncv's, looking for an existing row
1131d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            for (NamedContentValues namedContentValues: list) {
1132d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                Uri uri = namedContentValues.uri;
1133d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ContentValues cv = namedContentValues.values;
1134d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (Data.CONTENT_URI.equals(uri)) {
1135d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    String mimeType = cv.getAsString(Data.MIMETYPE);
1136d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (mimeType.equals(contentItemType)) {
1137d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        if (type != -1) {
1138d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            int subtype = cv.getAsInteger(Phone.TYPE);
1139d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            if (type != subtype) {
1140d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                continue;
1141d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            }
1142d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
1143d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        result.add(namedContentValues);
1144d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1145d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1146d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1147d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1148d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // If we've found an existing data row, we'll delete it.  Any rows left at the
1149d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // end should be deleted...
1150d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            for (NamedContentValues values : result) {
1151d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                list.remove(values);
1152d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1153d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1154d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Return the row found (or null)
1155d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return result;
1156d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1157d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1158d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        /**
1159d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * Create a wrapper for a builder (insert or update) that also includes the NCV for
1160d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * an existing row of this type.   If the SmartBuilder's cv field is not null, then
1161d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * it represents the current (old) values of this field.  The caller can then check
1162d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * whether the field is now different and needs to be updated; if it's not different,
1163d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * the caller will simply return and not generate a new CPO.  Otherwise, the builder
1164d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * should have its content values set, and the built CPO should be added to the
1165d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * ContactOperations list.
1166d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         *
1167d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param entity the contact entity (or null if this is a new contact)
1168d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param mimeType the mime type of this row
1169d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param type the subtype of this row
1170d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param stringType for groups, the name of the group (type will be ignored), or null
1171d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @return the created SmartBuilder
1172d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         */
1173d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public RowBuilder createBuilder(Entity entity, String mimeType, int type,
1174d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String stringType) {
1175d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = null;
1176d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1177d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (entity != null) {
1178d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                NamedContentValues ncv =
1179d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    findTypedData(entity.getSubValues(), mimeType, type, stringType);
1180d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (ncv != null) {
1181d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    builder = new RowBuilder(
1182d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            ContentProviderOperation
1183d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                .newUpdate(addCallerIsSyncAdapterParameter(
1184d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    dataUriFromNamedContentValues(ncv))),
1185d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            ncv);
1186d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1187d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1188d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1189d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (builder == null) {
1190d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                builder = newRowBuilder(entity, mimeType);
1191d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1192d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1193d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Return the appropriate builder (insert or update)
1194d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Caller will fill in the appropriate values; 4 MIMETYPE is already set
1195d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return builder;
1196d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1197d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1198d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private RowBuilder typedRowBuilder(Entity entity, String mimeType, int type) {
1199d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return createBuilder(entity, mimeType, type, null);
1200d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1201d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1202d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private RowBuilder untypedRowBuilder(Entity entity, String mimeType) {
1203d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return createBuilder(entity, mimeType, -1, null);
1204d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1205d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1206d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private RowBuilder newRowBuilder(Entity entity, String mimeType) {
1207d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // This is a new row; first get the contactId
1208d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // If the Contact is new, use the saved back value; otherwise the value in the entity
1209d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            int contactId = mContactBackValue;
1210d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (entity != null) {
1211d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                contactId = entity.getEntityValues().getAsInteger(RawContacts._ID);
1212d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1213d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1214d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Create an insert operation with the proper contactId reference
1215d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder =
1216d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                new RowBuilder(ContentProviderOperation.newInsert(
1217d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        addCallerIsSyncAdapterParameter(Data.CONTENT_URI)));
1218d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (entity == null) {
1219d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                builder.withValueBackReference(Data.RAW_CONTACT_ID, contactId);
1220d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } else {
1221d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                builder.withValue(Data.RAW_CONTACT_ID, contactId);
1222d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1223d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1224d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Set the mime type of the row
1225d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Data.MIMETYPE, mimeType);
1226d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return builder;
1227d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1228d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1229d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        /**
1230d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * Compare a column in a ContentValues with an (old) value, and see if they are the
1231d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * same.  For this purpose, null and an empty string are considered the same.
1232d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param cv a ContentValues object, from a NamedContentValues
1233d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param column a column that might be in the ContentValues
1234d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @param oldValue an old value (or null) to check against
1235d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * @return whether the column's value in the ContentValues matches oldValue
1236d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         */
1237d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private boolean cvCompareString(ContentValues cv, String column, String oldValue) {
1238d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv.containsKey(column)) {
1239d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (oldValue != null && cv.getAsString(column).equals(oldValue)) {
1240d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    return true;
1241d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1242d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } else if (oldValue == null || oldValue.length() == 0) {
1243d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return true;
1244d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1245d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return false;
1246d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1247d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1248d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addChildren(Entity entity, ArrayList<String> children) {
1249d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = untypedRowBuilder(entity, EasChildren.CONTENT_ITEM_TYPE);
1250d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            int i = 0;
1251d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            for (String child: children) {
1252d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                builder.withValue(EasChildren.ROWS[i++], child);
1253d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1254d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1255d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1256d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1257d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addGroup(Entity entity, String group) {
1258d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder =
1259d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                createBuilder(entity, GroupMembership.CONTENT_ITEM_TYPE, -1, group);
1260d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(GroupMembership.GROUP_SOURCE_ID, group);
1261d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1262d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1263d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1264d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addBirthday(Entity entity, String birthday) {
1265d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder =
1266d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    typedRowBuilder(entity, Event.CONTENT_ITEM_TYPE, Event.TYPE_BIRTHDAY);
1267d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1268d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, Event.START_DATE, birthday)) {
1269d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1270d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
12710969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            long millis = Utility.parseEmailDateTimeToMillis(birthday);
12720969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
12730969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            cal.setTimeInMillis(millis);
12740969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            if (cal.get(GregorianCalendar.HOUR_OF_DAY) >= 12) {
12750969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank                cal.add(GregorianCalendar.DATE, 1);
12760969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            }
12770969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            String realBirthday = CalendarUtilities.calendarToBirthdayString(cal);
12780969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank            builder.withValue(Event.START_DATE, realBirthday);
1279d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Event.TYPE, Event.TYPE_BIRTHDAY);
1280d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1281d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1282d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1283d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addName(Entity entity, String prefix, String givenName, String familyName,
1284d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String middleName, String suffix, String displayName, String yomiFirstName,
12850969df734735b0b55cc6a800335f665b40c8ecd6Marc Blank                String yomiLastName) {
1286d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = untypedRowBuilder(entity, StructuredName.CONTENT_ITEM_TYPE);
1287d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1288d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, StructuredName.GIVEN_NAME, givenName) &&
1289d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredName.FAMILY_NAME, familyName) &&
1290d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredName.MIDDLE_NAME, middleName) &&
1291d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredName.PREFIX, prefix) &&
1292d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredName.PHONETIC_GIVEN_NAME, yomiFirstName) &&
1293d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredName.PHONETIC_FAMILY_NAME, yomiLastName) &&
1294d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredName.SUFFIX, suffix)) {
1295d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1296d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1297d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredName.GIVEN_NAME, givenName);
1298d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredName.FAMILY_NAME, familyName);
1299d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredName.MIDDLE_NAME, middleName);
1300d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredName.SUFFIX, suffix);
1301d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, yomiFirstName);
1302d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredName.PHONETIC_FAMILY_NAME, yomiLastName);
1303d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredName.PREFIX, prefix);
1304d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1305d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1306d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1307d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addPersonal(Entity entity, EasPersonal personal) {
1308d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = untypedRowBuilder(entity, EasPersonal.CONTENT_ITEM_TYPE);
1309d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1310d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, EasPersonal.ANNIVERSARY, personal.anniversary) &&
1311d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, EasPersonal.FILE_AS , personal.fileAs)) {
1312d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1313d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1314d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (!personal.hasData()) {
1315d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1316d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1317d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(EasPersonal.FILE_AS, personal.fileAs);
1318d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(EasPersonal.ANNIVERSARY, personal.anniversary);
1319d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1320d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1321d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1322d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addBusiness(Entity entity, EasBusiness business) {
1323d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = untypedRowBuilder(entity, EasBusiness.CONTENT_ITEM_TYPE);
1324d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1325d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, EasBusiness.ACCOUNT_NAME, business.accountName) &&
1326d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, EasBusiness.CUSTOMER_ID, business.customerId) &&
1327d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, EasBusiness.GOVERNMENT_ID, business.governmentId)) {
1328d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1329d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1330d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (!business.hasData()) {
1331d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1332d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1333d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(EasBusiness.ACCOUNT_NAME, business.accountName);
1334d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(EasBusiness.CUSTOMER_ID, business.customerId);
1335d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(EasBusiness.GOVERNMENT_ID, business.governmentId);
1336d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1337d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1338d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1339d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addPhoto(Entity entity, String photo) {
1340d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = untypedRowBuilder(entity, Photo.CONTENT_ITEM_TYPE);
1341d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // We're always going to add this; it's not worth trying to figure out whether the
1342d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // picture is the same as the one stored.
1343d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            byte[] pic = Base64.decode(photo, Base64.DEFAULT);
1344d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Photo.PHOTO, pic);
1345d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1346d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1347d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1348d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addPhone(Entity entity, int type, String phone) {
1349d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = typedRowBuilder(entity, Phone.CONTENT_ITEM_TYPE, type);
1350d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1351d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, Phone.NUMBER, phone)) {
1352d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1353d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1354d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Phone.TYPE, type);
1355d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Phone.NUMBER, phone);
1356d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1357d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1358d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1359d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addWebpage(Entity entity, String url) {
1360d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = untypedRowBuilder(entity, Website.CONTENT_ITEM_TYPE);
1361d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1362d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, Website.URL, url)) {
1363d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1364d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1365d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Website.TYPE, Website.TYPE_WORK);
1366d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Website.URL, url);
1367d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1368d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1369d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1370d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addRelation(Entity entity, int type, String value) {
1371d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = typedRowBuilder(entity, Relation.CONTENT_ITEM_TYPE, type);
1372d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1373d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, Relation.DATA, value)) {
1374d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1375d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1376d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Relation.TYPE, type);
1377d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Relation.DATA, value);
1378d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1379d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1380d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1381d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addNickname(Entity entity, String name) {
1382d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder =
1383d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                typedRowBuilder(entity, Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE_DEFAULT);
1384d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1385d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, Nickname.NAME, name)) {
1386d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1387d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1388d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT);
1389d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Nickname.NAME, name);
1390d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1391d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1392d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1393d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addPostal(Entity entity, int type, String street, String city, String state,
1394d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String country, String code) {
1395d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = typedRowBuilder(entity, StructuredPostal.CONTENT_ITEM_TYPE,
1396d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    type);
1397d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1398d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, StructuredPostal.CITY, city) &&
1399d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredPostal.STREET, street) &&
1400d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredPostal.COUNTRY, country) &&
1401d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredPostal.POSTCODE, code) &&
1402d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, StructuredPostal.REGION, state)) {
1403d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1404d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1405d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredPostal.TYPE, type);
1406d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredPostal.CITY, city);
1407d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredPostal.STREET, street);
1408d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredPostal.COUNTRY, country);
1409d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredPostal.POSTCODE, code);
1410d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(StructuredPostal.REGION, state);
1411d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1412d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1413d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1414d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank       /**
1415d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * We now are dealing with up to maxRows typeless rows of mimeType data.  We need to try to
1416d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * match them with existing rows; if there's a match, everything's great.  Otherwise, we
1417d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * either need to add a new row for the data, or we have to replace an existing one
1418d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         * that no longer matches.  This is similar to the way Emails are handled.
1419d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank         */
1420d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addUntyped(Entity entity, ArrayList<UntypedRow> rows, String mimeType,
1421d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                int type, int maxRows) {
1422d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Make a list of all same type rows in the existing entity
1423d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<NamedContentValues> oldValues = EMPTY_ARRAY_NAMEDCONTENTVALUES;
1424d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<NamedContentValues> entityValues = EMPTY_ARRAY_NAMEDCONTENTVALUES;
1425d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (entity != null) {
1426d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                oldValues = findUntypedData(entityValues, type, mimeType);
1427d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                entityValues = entity.getSubValues();
1428d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1429d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1430d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // These will be rows needing replacement with new values
1431d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<UntypedRow> rowsToReplace = new ArrayList<UntypedRow>();
1432d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1433d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // The count of existing rows
1434d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            int numRows = oldValues.size();
1435d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            for (UntypedRow row: rows) {
1436d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                boolean found = false;
1437d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // If we already have this row, mark it
1438d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                for (NamedContentValues ncv: oldValues) {
1439d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    ContentValues cv = ncv.values;
1440d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    String data = cv.getAsString(COMMON_DATA_ROW);
1441d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    int rowType = -1;
1442d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (cv.containsKey(COMMON_TYPE_ROW)) {
1443d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        rowType = cv.getAsInteger(COMMON_TYPE_ROW);
1444d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1445d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (row.isSameAs(rowType, data)) {
1446d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        cv.put(FOUND_DATA_ROW, true);
1447d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // Remove this to indicate it's still being used
1448d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        entityValues.remove(ncv);
1449d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        found = true;
1450d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        break;
1451d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1452d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1453d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (!found) {
1454d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // If we don't, there are two possibilities
1455d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (numRows < maxRows) {
1456d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // If there are available rows, add a new one
1457d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        RowBuilder builder = newRowBuilder(entity, mimeType);
1458d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        row.addValues(builder);
1459d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        add(builder.build());
1460d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        numRows++;
1461d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else {
1462d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // Otherwise, say we need to replace a row with this
1463d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        rowsToReplace.add(row);
1464d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1465d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1466d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1467d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1468d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Go through rows needing replacement
1469d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            for (UntypedRow row: rowsToReplace) {
1470d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                for (NamedContentValues ncv: oldValues) {
1471d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    ContentValues cv = ncv.values;
1472d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // Find a row that hasn't been used (i.e. doesn't match current rows)
1473d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (!cv.containsKey(FOUND_DATA_ROW)) {
1474d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // And update it
1475d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        RowBuilder builder = new RowBuilder(
1476d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                ContentProviderOperation
1477d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    .newUpdate(addCallerIsSyncAdapterParameter(
1478d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                        dataUriFromNamedContentValues(ncv))),
1479d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                ncv);
1480d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        row.addValues(builder);
1481d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        add(builder.build());
1482d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1483d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1484d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1485d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1486d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1487d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addOrganization(Entity entity, int type, String company, String title,
1488d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String department, String yomiCompanyName, String officeLocation) {
1489d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = typedRowBuilder(entity, Organization.CONTENT_ITEM_TYPE, type);
1490d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1491d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, Organization.COMPANY, company) &&
1492d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, Organization.PHONETIC_NAME, yomiCompanyName) &&
1493d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, Organization.DEPARTMENT, department) &&
1494d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, Organization.TITLE, title) &&
1495d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cvCompareString(cv, Organization.OFFICE_LOCATION, officeLocation)) {
1496d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1497d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1498d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Organization.TYPE, type);
1499d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Organization.COMPANY, company);
1500d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Organization.TITLE, title);
1501d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Organization.DEPARTMENT, department);
1502d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Organization.PHONETIC_NAME, yomiCompanyName);
1503d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Organization.OFFICE_LOCATION, officeLocation);
1504d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1505d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1506d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1507d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public void addNote(Entity entity, String note) {
1508d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            RowBuilder builder = typedRowBuilder(entity, Note.CONTENT_ITEM_TYPE, -1);
1509d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues cv = builder.cv;
1510d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (note == null) return;
1511d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            note = note.replaceAll("\r\n", "\n");
1512d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv != null && cvCompareString(cv, Note.NOTE, note)) {
1513d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                return;
1514d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1515d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1516d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Reject notes with nothing in them.  Often, we get something from Outlook when
1517d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // nothing was ever entered.  Sigh.
1518d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            int len = note.length();
1519d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            int i = 0;
1520d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            for (; i < len; i++) {
1521d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                char c = note.charAt(i);
1522d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (!Character.isWhitespace(c)) {
1523d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    break;
1524d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1525d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1526d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (i == len) return;
1527d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1528d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            builder.withValue(Note.NOTE, note);
1529d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            add(builder.build());
1530d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1531d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1532d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1533d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    /**
1534d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * Generate the uri for the data row associated with this NamedContentValues object
1535d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * @param ncv the NamedContentValues object
1536d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * @return a uri that can be used to refer to this row
1537d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     */
1538d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public Uri dataUriFromNamedContentValues(NamedContentValues ncv) {
1539d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        long id = ncv.values.getAsLong(RawContacts._ID);
1540d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Uri dataUri = ContentUris.withAppendedId(ncv.uri, id);
1541d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return dataUri;
1542d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1543d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1544d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
1545d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public void cleanup() {
1546d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Mark the changed contacts dirty = 0
1547d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Permanently delete the user deletions
1548d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContactOperations ops = new ContactOperations();
1549d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        for (Long id: mUpdatedIdList) {
1550d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.add(ContentProviderOperation
1551d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    .newUpdate(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)
1552d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .buildUpon()
1553d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
1554d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .build())
1555d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    .withValue(RawContacts.DIRTY, 0).build());
1556d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1557d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        for (Long id: mDeletedIdList) {
1558d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ops.add(ContentProviderOperation
1559d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    .newDelete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)
1560d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .buildUpon()
1561d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
1562d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            .build())
1563d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    .build());
1564d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1565d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ops.execute();
1566d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContentResolver cr = mContext.getContentResolver();
1567d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (mGroupsUsed) {
1568d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Make sure the title column is set for all of our groups
1569d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // And that all of our groups are visible
1570d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // TODO Perhaps the visible part should only happen when the group is created, but
1571d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // this is fine for now.
1572d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Uri groupsUri = uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI);
1573d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Cursor c = cr.query(groupsUri, new String[] {Groups.SOURCE_ID, Groups.TITLE},
1574d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    Groups.TITLE + " IS NULL", null, null);
1575d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ContentValues values = new ContentValues();
1576d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            values.put(Groups.GROUP_VISIBLE, 1);
1577d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            try {
1578d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                while (c.moveToNext()) {
1579d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    String sourceId = c.getString(0);
1580d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    values.put(Groups.TITLE, sourceId);
1581d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cr.update(uriWithAccountAndIsSyncAdapter(groupsUri), values,
1582d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            Groups.SOURCE_ID + "=?", new String[] {sourceId});
1583d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1584d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } finally {
1585d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                c.close();
1586d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1587d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1588d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1589d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1590d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
1591d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public String getCollectionName() {
1592d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return "Contacts";
1593d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1594d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1595d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendEmail(Serializer s, ContentValues cv, int count, String displayName)
1596d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            throws IOException {
1597d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Get both parts of the email address (a newly created one in the UI won't have a name)
1598d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String addr = cv.getAsString(Email.DATA);
1599d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String name = cv.getAsString(Email.DISPLAY_NAME);
1600d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (name == null) {
1601d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (displayName != null) {
1602d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                name = displayName;
1603d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } else {
1604d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                name = addr;
1605d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1606d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1607d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Compose address from name and addr
1608d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (addr != null) {
1609d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String value;
1610d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Only send the raw email address for EAS 2.5 (Hotmail, in particular, chokes on
1611d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // an RFC822 address)
1612d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (mService.mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
1613d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                value = addr;
1614d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } else {
1615d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                value = '\"' + name + "\" <" + addr + '>';
1616d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1617d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (count < MAX_EMAIL_ROWS) {
1618d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(EMAIL_TAGS[count], value);
1619d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1620d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1621d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1622d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1623d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendIm(Serializer s, ContentValues cv, int count) throws IOException {
1624d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String value = cv.getAsString(Im.DATA);
1625d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (value == null) return;
1626d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (count < MAX_IM_ROWS) {
1627d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(IM_TAGS[count], value);
1628d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1629d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1630d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1631d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendOnePostal(Serializer s, ContentValues cv, int[] fieldNames)
1632d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            throws IOException{
1633d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredPostal.CITY, fieldNames[0]);
1634d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredPostal.COUNTRY, fieldNames[1]);
1635d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredPostal.POSTCODE, fieldNames[2]);
1636d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredPostal.REGION, fieldNames[3]);
1637d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredPostal.STREET, fieldNames[4]);
1638d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1639d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1640d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendStructuredPostal(Serializer s, ContentValues cv) throws IOException {
1641d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        switch (cv.getAsInteger(StructuredPostal.TYPE)) {
1642d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case StructuredPostal.TYPE_HOME:
1643d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                sendOnePostal(s, cv, HOME_ADDRESS_TAGS);
1644d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1645d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case StructuredPostal.TYPE_WORK:
1646d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                sendOnePostal(s, cv, WORK_ADDRESS_TAGS);
1647d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1648d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case StructuredPostal.TYPE_OTHER:
1649d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                sendOnePostal(s, cv, OTHER_ADDRESS_TAGS);
1650d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1651d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            default:
1652d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1653d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1654d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1655d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1656d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendStringData(Serializer s, ContentValues cv, String column, int tag)
1657d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            throws IOException {
1658d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (cv.containsKey(column)) {
1659d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String value = cv.getAsString(column);
1660d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (!TextUtils.isEmpty(value)) {
1661d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(tag, value);
1662d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1663d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1664d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1665d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1666d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private String sendStructuredName(Serializer s, ContentValues cv) throws IOException {
1667d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String displayName = null;
1668d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredName.FAMILY_NAME, Tags.CONTACTS_LAST_NAME);
1669d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredName.GIVEN_NAME, Tags.CONTACTS_FIRST_NAME);
1670d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredName.MIDDLE_NAME, Tags.CONTACTS_MIDDLE_NAME);
1671d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredName.SUFFIX, Tags.CONTACTS_SUFFIX);
1672d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredName.PHONETIC_GIVEN_NAME, Tags.CONTACTS_YOMI_FIRST_NAME);
1673d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredName.PHONETIC_FAMILY_NAME, Tags.CONTACTS_YOMI_LAST_NAME);
1674d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, StructuredName.PREFIX, Tags.CONTACTS_TITLE);
1675d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return displayName;
1676d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1677d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1678d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendBusiness(Serializer s, ContentValues cv) throws IOException {
1679d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, EasBusiness.ACCOUNT_NAME, Tags.CONTACTS2_ACCOUNT_NAME);
1680d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, EasBusiness.CUSTOMER_ID, Tags.CONTACTS2_CUSTOMER_ID);
1681d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, EasBusiness.GOVERNMENT_ID, Tags.CONTACTS2_GOVERNMENT_ID);
1682d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1683d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1684d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendPersonal(Serializer s, ContentValues cv) throws IOException {
1685d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, EasPersonal.ANNIVERSARY, Tags.CONTACTS_ANNIVERSARY);
1686d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, EasPersonal.FILE_AS, Tags.CONTACTS_FILE_AS);
1687d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1688d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1689d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendBirthday(Serializer s, ContentValues cv) throws IOException {
1690d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, Event.START_DATE, Tags.CONTACTS_BIRTHDAY);
1691d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1692d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1693d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendPhoto(Serializer s, ContentValues cv) throws IOException {
1694d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (cv.containsKey(Photo.PHOTO)) {
1695d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            byte[] bytes = cv.getAsByteArray(Photo.PHOTO);
1696d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String pic = Base64.encodeToString(bytes, Base64.NO_WRAP);
1697d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.CONTACTS_PICTURE, pic);
1698d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        } else {
1699d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Send an empty tag, which signals the server to delete any pre-existing photo
1700d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.tag(Tags.CONTACTS_PICTURE);
1701d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1702d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1703d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1704d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendOrganization(Serializer s, ContentValues cv) throws IOException {
1705d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, Organization.TITLE, Tags.CONTACTS_JOB_TITLE);
1706d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, Organization.COMPANY, Tags.CONTACTS_COMPANY_NAME);
1707d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, Organization.DEPARTMENT, Tags.CONTACTS_DEPARTMENT);
1708d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, Organization.OFFICE_LOCATION, Tags.CONTACTS_OFFICE_LOCATION);
1709d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1710d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1711d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendNickname(Serializer s, ContentValues cv) throws IOException {
1712d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, Nickname.NAME, Tags.CONTACTS2_NICKNAME);
1713d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1714d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1715d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendWebpage(Serializer s, ContentValues cv) throws IOException {
1716d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        sendStringData(s, cv, Website.URL, Tags.CONTACTS_WEBPAGE);
1717d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1718d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1719d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendNote(Serializer s, ContentValues cv) throws IOException {
1720d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Even when there is no local note, we must explicitly upsync an empty note,
1721d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // which is the only way to force the server to delete any pre-existing note.
1722d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String note = "";
1723d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (cv.containsKey(Note.NOTE)) {
1724d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // EAS won't accept note data with raw newline characters
1725d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            note = cv.getAsString(Note.NOTE).replaceAll("\n", "\r\n");
1726d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1727d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Format of upsync data depends on protocol version
1728d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
1729d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.start(Tags.BASE_BODY);
1730d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_TEXT).data(Tags.BASE_DATA, note);
1731d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.end();
1732d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        } else {
1733d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.CONTACTS_BODY, note);
1734d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1735d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1736d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1737d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendChildren(Serializer s, ContentValues cv) throws IOException {
1738d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        boolean first = true;
1739d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        for (int i = 0; i < EasChildren.MAX_CHILDREN; i++) {
1740d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            String row = EasChildren.ROWS[i];
1741d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (cv.containsKey(row)) {
1742d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (first) {
1743d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    s.start(Tags.CONTACTS_CHILDREN);
1744d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    first = false;
1745d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1746d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_CHILD, cv.getAsString(row));
1747d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1748d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1749d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (!first) {
1750d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.end();
1751d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1752d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1753d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1754d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendPhone(Serializer s, ContentValues cv, int workCount, int homeCount)
1755d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            throws IOException {
1756d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String value = cv.getAsString(Phone.NUMBER);
1757d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (value == null) return;
1758d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        switch (cv.getAsInteger(Phone.TYPE)) {
1759d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_WORK:
1760d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (workCount < MAX_PHONE_ROWS) {
1761d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    s.data(WORK_PHONE_TAGS[workCount], value);
1762d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1763d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1764d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_MMS:
1765d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS2_MMS, value);
1766d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1767d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_ASSISTANT:
1768d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_ASSISTANT_TELEPHONE_NUMBER, value);
1769d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1770d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_FAX_WORK:
1771d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_BUSINESS_FAX_NUMBER, value);
1772d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1773d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_COMPANY_MAIN:
1774d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS2_COMPANY_MAIN_PHONE, value);
1775d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1776d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_HOME:
1777d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (homeCount < MAX_PHONE_ROWS) {
1778d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    s.data(HOME_PHONE_TAGS[homeCount], value);
1779d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1780d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1781d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_MOBILE:
1782d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_MOBILE_TELEPHONE_NUMBER, value);
1783d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1784d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_CAR:
1785d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_CAR_TELEPHONE_NUMBER, value);
1786d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1787d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_PAGER:
1788d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_PAGER_NUMBER, value);
1789d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1790d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_RADIO:
1791d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_RADIO_TELEPHONE_NUMBER, value);
1792d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1793d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Phone.TYPE_FAX_HOME:
1794d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_HOME_FAX_NUMBER, value);
1795d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1796d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            default:
1797d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1798d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1799d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1800d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1801d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void sendRelation(Serializer s, ContentValues cv) throws IOException {
1802d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String value = cv.getAsString(Relation.DATA);
1803d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (value == null) return;
1804d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        switch (cv.getAsInteger(Relation.TYPE)) {
1805d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Relation.TYPE_ASSISTANT:
1806d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_ASSISTANT_NAME, value);
1807d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1808d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Relation.TYPE_MANAGER:
1809d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS2_MANAGER_NAME, value);
1810d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1811d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            case Relation.TYPE_SPOUSE:
1812d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.CONTACTS_SPOUSE, value);
1813d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1814d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            default:
1815d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                break;
1816d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1817d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1818d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1819d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private void dirtyContactsWithinDirtyGroups() {
1820d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContentResolver cr = mService.mContentResolver;
1821d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Cursor c = cr.query(uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI),
1822d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                GROUPS_ID_PROJECTION, Groups.DIRTY + "=1", null, null);
1823d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        try {
1824d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (c.getCount() > 0) {
1825d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String[] updateArgs = new String[1];
1826d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ContentValues updateValues = new ContentValues();
1827d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                while (c.moveToNext()) {
1828d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // For each, "touch" all data rows with this group id; this will mark contacts
1829d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // in this group as dirty (per ContactsContract).  We will then know to upload
1830d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // them to the server with the modified group information
1831d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    long id = c.getLong(0);
1832d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    updateValues.put(GroupMembership.GROUP_ROW_ID, id);
1833d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    updateArgs[0] = Long.toString(id);
1834d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cr.update(Data.CONTENT_URI, updateValues,
1835d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            MIMETYPE_GROUP_MEMBERSHIP_AND_ID_EQUALS, updateArgs);
1836d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1837d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // Really delete groups that are marked deleted
1838d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                cr.delete(uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI), Groups.DELETED + "=1",
1839d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        null);
1840d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // Clear the dirty flag for all of our groups
1841d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                updateValues.clear();
1842d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                updateValues.put(Groups.DIRTY, 0);
1843d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                cr.update(uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI), updateValues, null,
1844d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        null);
1845d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1846d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        } finally {
1847d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            c.close();
1848d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1849d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
1850d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1851d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    @Override
1852d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public boolean sendLocalChanges(Serializer s) throws IOException {
1853d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContentResolver cr = mService.mContentResolver;
1854d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1855d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Find any groups of ours that are dirty and dirty those groups' members
1856d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        dirtyContactsWithinDirtyGroups();
1857d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1858d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // First, let's find Contacts that have changed.
1859d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Uri uri = uriWithAccountAndIsSyncAdapter(RawContactsEntity.CONTENT_URI);
1860d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (getSyncKey().equals("0")) {
1861d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return false;
1862d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1863d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1864d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Get them all atomically
1865d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        EntityIterator ei = RawContacts.newEntityIterator(
1866d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                cr.query(uri, null, RawContacts.DIRTY + "=1", null, null));
1867d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContentValues cidValues = new ContentValues();
1868d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        try {
1869d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            boolean first = true;
1870d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            final Uri rawContactUri = addCallerIsSyncAdapterParameter(RawContacts.CONTENT_URI);
1871d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (ei.hasNext()) {
1872d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                Entity entity = ei.next();
1873d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // For each of these entities, create the change commands
1874d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ContentValues entityValues = entity.getEntityValues();
1875d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String serverId = entityValues.getAsString(RawContacts.SOURCE_ID);
1876d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ArrayList<Integer> groupIds = new ArrayList<Integer>();
1877d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (first) {
1878d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    s.start(Tags.SYNC_COMMANDS);
1879d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    userLog("Sending Contacts changes to the server");
1880d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    first = false;
1881d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1882d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (serverId == null) {
1883d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // This is a new contact; create a clientId
1884d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    String clientId = "new_" + mMailbox.mId + '_' + System.currentTimeMillis();
1885d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    userLog("Creating new contact with clientId: ", clientId);
1886d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    s.start(Tags.SYNC_ADD).data(Tags.SYNC_CLIENT_ID, clientId);
1887d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    // And save it in the raw contact
1888d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cidValues.put(RawContacts.SYNC1, clientId);
1889d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    cr.update(ContentUris.
1890d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            withAppendedId(rawContactUri,
1891d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    entityValues.getAsLong(RawContacts._ID)),
1892d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    cidValues, null, null);
1893d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
1894d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (entityValues.getAsInteger(RawContacts.DELETED) == 1) {
1895d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        userLog("Deleting contact with serverId: ", serverId);
1896d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end();
1897d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        mDeletedIdList.add(entityValues.getAsLong(RawContacts._ID));
1898d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        continue;
1899d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1900d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    userLog("Upsync change to contact with serverId: " + serverId);
1901d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    s.start(Tags.SYNC_CHANGE).data(Tags.SYNC_SERVER_ID, serverId);
1902d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1903d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.start(Tags.SYNC_APPLICATION_DATA);
1904d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // Write out the data here
1905d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                int imCount = 0;
1906d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                int emailCount = 0;
1907d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                int homePhoneCount = 0;
1908d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                int workPhoneCount = 0;
1909d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                String displayName = null;
1910d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ArrayList<ContentValues> emailValues = new ArrayList<ContentValues>();
1911d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                for (NamedContentValues ncv: entity.getSubValues()) {
1912d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    ContentValues cv = ncv.values;
1913d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    String mimeType = cv.getAsString(Data.MIMETYPE);
1914d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (mimeType.equals(Email.CONTENT_ITEM_TYPE)) {
1915d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        emailValues.add(cv);
1916d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Nickname.CONTENT_ITEM_TYPE)) {
1917d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendNickname(s, cv);
1918d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(EasChildren.CONTENT_ITEM_TYPE)) {
1919d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendChildren(s, cv);
1920d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(EasBusiness.CONTENT_ITEM_TYPE)) {
1921d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendBusiness(s, cv);
1922d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Website.CONTENT_ITEM_TYPE)) {
1923d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendWebpage(s, cv);
1924d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(EasPersonal.CONTENT_ITEM_TYPE)) {
1925d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendPersonal(s, cv);
1926d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) {
1927d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendPhone(s, cv, workPhoneCount, homePhoneCount);
1928d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        int type = cv.getAsInteger(Phone.TYPE);
1929d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        if (type == Phone.TYPE_HOME) homePhoneCount++;
1930d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        if (type == Phone.TYPE_WORK) workPhoneCount++;
1931d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Relation.CONTENT_ITEM_TYPE)) {
1932d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendRelation(s, cv);
1933d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) {
1934d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        displayName = sendStructuredName(s, cv);
1935d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(StructuredPostal.CONTENT_ITEM_TYPE)) {
1936d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendStructuredPostal(s, cv);
1937d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Organization.CONTENT_ITEM_TYPE)) {
1938d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendOrganization(s, cv);
1939d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Im.CONTENT_ITEM_TYPE)) {
1940d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendIm(s, cv, imCount++);
1941d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Event.CONTENT_ITEM_TYPE)) {
1942d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        Integer eventType = cv.getAsInteger(Event.TYPE);
1943d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        if (eventType != null && eventType.equals(Event.TYPE_BIRTHDAY)) {
1944d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            sendBirthday(s, cv);
1945d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
1946d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(GroupMembership.CONTENT_ITEM_TYPE)) {
1947d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // We must gather these, and send them together (below)
1948d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        groupIds.add(cv.getAsInteger(GroupMembership.GROUP_ROW_ID));
1949d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Note.CONTENT_ITEM_TYPE)) {
1950d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendNote(s, cv);
1951d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else if (mimeType.equals(Photo.CONTENT_ITEM_TYPE)) {
1952d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sendPhoto(s, cv);
1953d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } else {
1954d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        userLog("Contacts upsync, unknown data: ", mimeType);
1955d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1956d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1957d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1958d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // We do the email rows last, because we need to make sure we've found the
1959d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // displayName (if one exists); this would be in a StructuredName rnow
1960d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                for (ContentValues cv: emailValues) {
1961d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    sendEmail(s, cv, emailCount++, displayName);
1962d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1963d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1964d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // Now, we'll send up groups, if any
1965d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (!groupIds.isEmpty()) {
1966d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    boolean groupFirst = true;
1967d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    for (int id: groupIds) {
1968d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        // Since we get id's from the provider, we need to find their names
1969d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        Cursor c = cr.query(ContentUris.withAppendedId(Groups.CONTENT_URI, id),
1970d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                GROUP_TITLE_PROJECTION, null, null, null);
1971d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        try {
1972d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            // Presumably, this should always succeed, but ...
1973d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            if (c.moveToFirst()) {
1974d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                if (groupFirst) {
1975d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    s.start(Tags.CONTACTS_CATEGORIES);
1976d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                    groupFirst = false;
1977d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                }
1978d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                                s.data(Tags.CONTACTS_CATEGORY, c.getString(0));
1979d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            }
1980d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        } finally {
1981d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                            c.close();
1982d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        }
1983d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1984d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (!groupFirst) {
1985d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        s.end();
1986d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
1987d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
1988d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.end().end(); // ApplicationData & Change
1989d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                mUpdatedIdList.add(entityValues.getAsLong(RawContacts._ID));
1990d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1991d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (!first) {
1992d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.end(); // Commands
1993d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
1994d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        } finally {
1995d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ei.close();
1996d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
1997d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
1998d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return false;
1999d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
2000d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank}
2001