diff --git a/src/com/android/exchange/ContactsSyncAdapterService.java b/src/com/android/exchange/ContactsSyncAdapterService.java index 9741e42c8..311dd4f14 100644 --- a/src/com/android/exchange/ContactsSyncAdapterService.java +++ b/src/com/android/exchange/ContactsSyncAdapterService.java @@ -17,7 +17,6 @@ package com.android.exchange; import com.android.email.Email; -import com.android.email.provider.EmailContent; import com.android.email.provider.EmailContent.AccountColumns; import com.android.email.provider.EmailContent.Mailbox; import com.android.email.provider.EmailContent.MailboxColumns; @@ -35,6 +34,7 @@ import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; +import android.provider.ContactsContract.Groups; import android.provider.ContactsContract.RawContacts; import android.util.Log; @@ -43,7 +43,7 @@ public class ContactsSyncAdapterService extends Service { private static SyncAdapterImpl sSyncAdapter = null; private static final Object sSyncAdapterLock = new Object(); - private static final String[] ID_PROJECTION = new String[] {EmailContent.RECORD_ID}; + private static final String[] ID_PROJECTION = new String[] {"_id"}; private static final String ACCOUNT_AND_TYPE_CONTACTS = MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CONTACTS; @@ -85,6 +85,15 @@ public class ContactsSyncAdapterService extends Service { return sSyncAdapter.getSyncAdapterBinder(); } + private static boolean hasDirtyRows(ContentResolver resolver, Uri uri, String dirtyColumn) { + Cursor c = resolver.query(uri, ID_PROJECTION, dirtyColumn + "=1", null, null); + try { + return c.getCount() > 0; + } finally { + c.close(); + } + } + /** * Partial integration with system SyncManager; we tell our EAS ExchangeService to start a * contacts sync when we get the signal from SyncManager. @@ -95,21 +104,29 @@ public class ContactsSyncAdapterService extends Service { String authority, ContentProviderClient provider, SyncResult syncResult) throws OperationCanceledException { ContentResolver cr = context.getContentResolver(); - Log.i(TAG, "performSync"); + if (Email.DEBUG) { + Log.d(TAG, "performSync"); + } + + // If we've been asked to do an upload, make sure we've got work to do if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) { Uri uri = RawContacts.CONTENT_URI.buildUpon() .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) .appendQueryParameter(RawContacts.ACCOUNT_TYPE, Email.EXCHANGE_ACCOUNT_MANAGER_TYPE) .build(); - Cursor c = cr.query(uri, - new String[] {RawContacts._ID}, RawContacts.DIRTY + "=1", null, null); - try { - if (!c.moveToFirst()) { - Log.i(TAG, "Upload sync; no changes"); - return; - } - } finally { - c.close(); + // See if we've got dirty contacts or dirty groups containing our contacts + boolean changed = hasDirtyRows(cr, uri, RawContacts.DIRTY); + if (!changed) { + uri = Groups.CONTENT_URI.buildUpon() + .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) + .appendQueryParameter(RawContacts.ACCOUNT_TYPE, + Email.EXCHANGE_ACCOUNT_MANAGER_TYPE) + .build(); + changed = hasDirtyRows(cr, uri, Groups.DIRTY); + } + if (!changed) { + Log.i(TAG, "Upload sync; no changes"); + return; } } diff --git a/src/com/android/exchange/adapter/ContactsSyncAdapter.java b/src/com/android/exchange/adapter/ContactsSyncAdapter.java index 8a3617eb0..cc65a2623 100644 --- a/src/com/android/exchange/adapter/ContactsSyncAdapter.java +++ b/src/com/android/exchange/adapter/ContactsSyncAdapter.java @@ -75,7 +75,10 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter { private static final String SERVER_ID_SELECTION = RawContacts.SOURCE_ID + "=?"; private static final String CLIENT_ID_SELECTION = RawContacts.SYNC1 + "=?"; private static final String[] ID_PROJECTION = new String[] {RawContacts._ID}; - private static final String[] GROUP_PROJECTION = new String[] {Groups.SOURCE_ID}; + private static final String[] GROUP_TITLE_PROJECTION = new String[] {Groups.TITLE}; + private static final String MIMETYPE_GROUP_MEMBERSHIP_AND_ID_EQUALS = Data.MIMETYPE + "='" + + GroupMembership.CONTENT_ITEM_TYPE + "' AND " + GroupMembership.GROUP_ROW_ID + "=?"; + private static final String[] GROUPS_ID_PROJECTION = new String[] {Groups._ID}; private static final ArrayList EMPTY_ARRAY_NAMEDCONTENTVALUES = new ArrayList(); @@ -1742,17 +1745,47 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter { } } + private void dirtyContactsWithinDirtyGroups() { + ContentResolver cr = mService.mContentResolver; + Cursor c = cr.query(uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI), + GROUPS_ID_PROJECTION, Groups.DIRTY + "=1", null, null); + try { + if (c.getCount() > 0) { + String[] updateArgs = new String[1]; + ContentValues updateValues = new ContentValues(); + while (c.moveToNext()) { + // For each, "touch" all data rows with this group id; this will mark contacts + // in this group as dirty (per ContactsContract). We will then know to upload + // them to the server with the modified group information + long id = c.getLong(0); + updateValues.put(GroupMembership.GROUP_ROW_ID, id); + updateArgs[0] = Long.toString(id); + cr.update(Data.CONTENT_URI, updateValues, + MIMETYPE_GROUP_MEMBERSHIP_AND_ID_EQUALS, updateArgs); + } + // Really delete groups that are marked deleted + cr.delete(uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI), Groups.DELETED + "=1", + null); + // Clear the dirty flag for all of our groups + updateValues.clear(); + updateValues.put(Groups.DIRTY, 0); + cr.update(uriWithAccountAndIsSyncAdapter(Groups.CONTENT_URI), updateValues, null, + null); + } + } finally { + c.close(); + } + } + @Override public boolean sendLocalChanges(Serializer s) throws IOException { - // First, let's find Contacts that have changed. ContentResolver cr = mService.mContentResolver; - Uri uri = RawContactsEntity.CONTENT_URI.buildUpon() - .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccount.mEmailAddress) - .appendQueryParameter(RawContacts.ACCOUNT_TYPE, - com.android.email.Email.EXCHANGE_ACCOUNT_MANAGER_TYPE) - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .build(); + // Find any groups of ours that are dirty and dirty those groups' members + dirtyContactsWithinDirtyGroups(); + + // First, let's find Contacts that have changed. + Uri uri = uriWithAccountAndIsSyncAdapter(RawContactsEntity.CONTENT_URI); if (getSyncKey().equals("0")) { return false; } @@ -1863,7 +1896,7 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter { for (int id: groupIds) { // Since we get id's from the provider, we need to find their names Cursor c = cr.query(ContentUris.withAppendedId(Groups.CONTENT_URI, id), - GROUP_PROJECTION, null, null, null); + GROUP_TITLE_PROJECTION, null, null, null); try { // Presumably, this should always succeed, but ... if (c.moveToFirst()) {