diff options
author | Dave Santoro <dsantoro@google.com> | 2011-07-13 14:03:53 -0700 |
---|---|---|
committer | Dave Santoro <dsantoro@google.com> | 2011-07-27 17:15:46 -0700 |
commit | 43368a3f9e05a979e454e278d6a0e8475f08923d (patch) | |
tree | 3ff616a1aff1af5ea4d18d437b9ac8f2032e985a | |
parent | 51d1da962292ecef21abdf7e41abfdb7f1d72fcd (diff) | |
download | packages_providers_ContactsProvider-43368a3f9e05a979e454e278d6a0e8475f08923d.zip packages_providers_ContactsProvider-43368a3f9e05a979e454e278d6a0e8475f08923d.tar.gz packages_providers_ContactsProvider-43368a3f9e05a979e454e278d6a0e8475f08923d.tar.bz2 |
Provider and DB changes to support data_set field.
The intent of the data set field is to provide a way for multiple
sync adapters from the same account name + type to manage separate
sets of data in the raw_contacts and groups table.
For example, this would allow for Focus groups to be synced in from
Focus via the Google Contacts sync adapter, and for Google+ Circles
to be synced in from the Google+ app, even though both are tied to
the same account name + type.
Bug 5077096
Change-Id: I641c5d233d8d4d70988d209179c4e79bdb9c7ea1
10 files changed, 526 insertions, 160 deletions
diff --git a/src/com/android/providers/contacts/AccountWithDataSet.java b/src/com/android/providers/contacts/AccountWithDataSet.java new file mode 100644 index 0000000..d667432 --- /dev/null +++ b/src/com/android/providers/contacts/AccountWithDataSet.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.providers.contacts; + +import com.android.internal.util.Objects; + +/** + * Account information that includes the data set, if any. + */ +public class AccountWithDataSet { + private final String mAccountName; + private final String mAccountType; + private final String mDataSet; + + public AccountWithDataSet(String accountName, String accountType, String dataSet) { + mAccountName = accountName; + mAccountType = accountType; + mDataSet = dataSet; + } + + public String getAccountName() { + return mAccountName; + } + + public String getAccountType() { + return mAccountType; + } + + public String getDataSet() { + return mDataSet; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof AccountWithDataSet) { + AccountWithDataSet other = (AccountWithDataSet) obj; + return Objects.equal(mAccountName, other.getAccountName()) + && Objects.equal(mAccountType, other.getAccountType()) + && Objects.equal(mDataSet, other.getDataSet()); + } + return false; + } + + @Override + public int hashCode() { + int result = mAccountName != null ? mAccountName.hashCode() : 0; + result = 31 * result + (mAccountType != null ? mAccountType.hashCode() : 0); + result = 31 * result + (mDataSet != null ? mDataSet.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "AccountWithDataSet {name=" + mAccountName + ", type=" + mAccountType + ", dataSet=" + + mDataSet + "}"; + } +} diff --git a/src/com/android/providers/contacts/ContactAggregator.java b/src/com/android/providers/contacts/ContactAggregator.java index a2954ae..5064ee7 100644 --- a/src/com/android/providers/contacts/ContactAggregator.java +++ b/src/com/android/providers/contacts/ContactAggregator.java @@ -28,10 +28,9 @@ import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns; import com.android.providers.contacts.ContactsDatabaseHelper.Tables; +import com.android.providers.contacts.ContactsDatabaseHelper.Views; -import android.accounts.Account; import android.content.ContentValues; -import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; @@ -139,6 +138,7 @@ public class ContactAggregator { private SQLiteStatement mPhotoIdUpdate; private SQLiteStatement mDisplayNameUpdate; private SQLiteStatement mProfileUpdate; + private SQLiteStatement mNoDataSetProfileUpdate; private SQLiteStatement mNoAccountProfileUpdate; private SQLiteStatement mHasPhoneNumberUpdate; private SQLiteStatement mLookupKeyUpdate; @@ -154,6 +154,7 @@ public class ContactAggregator { private String[] mSelectionArgs1 = new String[1]; private String[] mSelectionArgs2 = new String[2]; private String[] mSelectionArgs3 = new String[3]; + private String[] mSelectionArgs4 = new String[4]; private long mMimeTypeIdEmail; private long mMimeTypeIdPhoto; private long mMimeTypeIdPhone; @@ -327,13 +328,22 @@ public class ContactAggregator { "UPDATE " + Tables.ACCOUNTS + " SET " + AccountsColumns.PROFILE_RAW_CONTACT_ID + "=? " + " WHERE " + AccountsColumns.ACCOUNT_TYPE + "=?" + - " AND " + AccountsColumns.ACCOUNT_NAME + "=?"); + " AND " + AccountsColumns.ACCOUNT_NAME + "=?" + + " AND " + AccountsColumns.DATA_SET + "=?"); + + mNoDataSetProfileUpdate = db.compileStatement( + "UPDATE " + Tables.ACCOUNTS + + " SET " + AccountsColumns.PROFILE_RAW_CONTACT_ID + "=? " + + " WHERE " + AccountsColumns.ACCOUNT_TYPE + "=?" + + " AND " + AccountsColumns.ACCOUNT_NAME + "=?" + + " AND " + AccountsColumns.DATA_SET + " IS NULL"); mNoAccountProfileUpdate = db.compileStatement( "UPDATE " + Tables.ACCOUNTS + " SET " + AccountsColumns.PROFILE_RAW_CONTACT_ID + "=? " + " WHERE " + AccountsColumns.ACCOUNT_TYPE + " IS NULL" + - " AND " + AccountsColumns.ACCOUNT_NAME + " IS NULL"); + " AND " + AccountsColumns.ACCOUNT_NAME + " IS NULL" + + " AND " + AccountsColumns.DATA_SET + " IS NULL"); mLookupKeyUpdate = db.compileStatement( "UPDATE " + Tables.CONTACTS + @@ -406,6 +416,7 @@ public class ContactAggregator { String SQL = "SELECT " + RawContacts._ID + "," + RawContacts.CONTACT_ID + ", " + RawContacts.ACCOUNT_TYPE + "," + RawContacts.ACCOUNT_NAME + + ", " + RawContacts.DATA_SET + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + " IN("; @@ -413,6 +424,7 @@ public class ContactAggregator { int CONTACT_ID = 1; int ACCOUNT_TYPE = 2; int ACCOUNT_NAME = 3; + int DATA_SET = 4; } /** @@ -451,6 +463,7 @@ public class ContactAggregator { long contactIds[] = new long[count]; String accountTypes[] = new String[count]; String accountNames[] = new String[count]; + String dataSets[] = new String[count]; Cursor c = db.rawQuery(mSb.toString(), selectionArgs); try { count = c.getCount(); @@ -460,6 +473,7 @@ public class ContactAggregator { contactIds[index] = c.getLong(AggregationQuery.CONTACT_ID); accountTypes[index] = c.getString(AggregationQuery.ACCOUNT_TYPE); accountNames[index] = c.getString(AggregationQuery.ACCOUNT_NAME); + dataSets[index] = c.getString(AggregationQuery.DATA_SET); index++; } } finally { @@ -468,7 +482,7 @@ public class ContactAggregator { for (int i = 0; i < count; i++) { aggregateContact(txContext, db, rawContactIds[i], accountTypes[i], accountNames[i], - contactIds[i], mCandidates, mMatcher, mValues); + dataSets[i], contactIds[i], mCandidates, mMatcher, mValues); } long elapsedTime = System.currentTimeMillis() - start; @@ -588,7 +602,7 @@ public class ContactAggregator { * profile contact). */ public void onProfileRawContactInsert(TransactionContext txContext, SQLiteDatabase db, - long rawContactId, Account account) { + long rawContactId, AccountWithDataSet accountWithDataSet) { long profileContactId = 0; try { profileContactId = mProfileContactIdQuery.simpleQueryForLong(); @@ -601,13 +615,19 @@ public class ContactAggregator { } // Update the accounts table with the user's raw contact ID. - if (account == null) { + if (accountWithDataSet == null) { mNoAccountProfileUpdate.bindLong(1, rawContactId); mNoAccountProfileUpdate.execute(); + } else if (accountWithDataSet.getDataSet() == null) { + mNoDataSetProfileUpdate.bindLong(1, rawContactId); + mNoDataSetProfileUpdate.bindString(2, accountWithDataSet.getAccountType()); + mNoDataSetProfileUpdate.bindString(3, accountWithDataSet.getAccountName()); + mNoDataSetProfileUpdate.execute(); } else { mProfileUpdate.bindLong(1, rawContactId); - mProfileUpdate.bindString(2, account.type); - mProfileUpdate.bindString(3, account.name); + mProfileUpdate.bindString(2, accountWithDataSet.getAccountType()); + mProfileUpdate.bindString(3, accountWithDataSet.getAccountName()); + mProfileUpdate.bindString(4, accountWithDataSet.getDataSet()); mProfileUpdate.execute(); } } @@ -616,13 +636,16 @@ public class ContactAggregator { public static final String TABLE = Tables.RAW_CONTACTS; public static final String[] COLUMNS = { - RawContacts.CONTACT_ID, RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_NAME }; + RawContacts.CONTACT_ID, RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_NAME, + RawContacts.DATA_SET + }; public static final String SELECTION = RawContacts._ID + "=?"; public static final int CONTACT_ID = 0; public static final int ACCOUNT_TYPE = 1; public static final int ACCOUNT_NAME = 2; + public static final int DATA_SET = 3; } public void aggregateContact( @@ -638,6 +661,7 @@ public class ContactAggregator { long contactId = 0; String accountName = null; String accountType = null; + String dataSet = null; mSelectionArgs1[0] = String.valueOf(rawContactId); Cursor cursor = db.query(RawContactIdAndAccountQuery.TABLE, RawContactIdAndAccountQuery.COLUMNS, RawContactIdAndAccountQuery.SELECTION, @@ -647,12 +671,13 @@ public class ContactAggregator { contactId = cursor.getLong(RawContactIdAndAccountQuery.CONTACT_ID); accountType = cursor.getString(RawContactIdAndAccountQuery.ACCOUNT_TYPE); accountName = cursor.getString(RawContactIdAndAccountQuery.ACCOUNT_NAME); + dataSet = cursor.getString(RawContactIdAndAccountQuery.DATA_SET); } } finally { cursor.close(); } - aggregateContact(txContext, db, rawContactId, accountType, accountName, contactId, + aggregateContact(txContext, db, rawContactId, accountType, accountName, dataSet, contactId, candidates, matcher, values); } @@ -691,8 +716,9 @@ public class ContactAggregator { * with the highest match score. If no such contact is found, creates a new contact. */ private synchronized void aggregateContact(TransactionContext txContext, SQLiteDatabase db, - long rawContactId, String accountType, String accountName, long currentContactId, - MatchCandidateList candidates, ContactMatcher matcher, ContentValues values) { + long rawContactId, String accountType, String accountName, String dataSet, + long currentContactId, MatchCandidateList candidates, ContactMatcher matcher, + ContentValues values) { int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT; @@ -722,7 +748,8 @@ public class ContactAggregator { // the same account, not only will we not join it, but also we will split // that other aggregate if (contactId != -1 && contactId != currentContactId && - containsRawContactsFromAccount(db, contactId, accountType, accountName)) { + containsRawContactsFromAccount(db, contactId, accountType, accountName, + dataSet)) { contactIdToSplit = contactId; contactId = -1; } @@ -785,25 +812,39 @@ public class ContactAggregator { * Returns true if the aggregate contains has any raw contacts from the specified account. */ private boolean containsRawContactsFromAccount( - SQLiteDatabase db, long contactId, String accountType, String accountName) { + SQLiteDatabase db, long contactId, String accountType, String accountName, + String dataSet) { String query; String[] args; if (accountType == null) { query = "SELECT count(_id) FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts.CONTACT_ID + "=?" + " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL " + - " AND " + RawContacts.ACCOUNT_NAME + " IS NULL "; + " AND " + RawContacts.ACCOUNT_NAME + " IS NULL " + + " AND " + RawContacts.DATA_SET + " IS NULL"; args = mSelectionArgs1; args[0] = String.valueOf(contactId); - } else { + } else if (dataSet == null) { query = "SELECT count(_id) FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts.CONTACT_ID + "=?" + " AND " + RawContacts.ACCOUNT_TYPE + "=?" + - " AND " + RawContacts.ACCOUNT_NAME + "=?"; + " AND " + RawContacts.ACCOUNT_NAME + "=?" + + " AND " + RawContacts.DATA_SET + " IS NULL"; args = mSelectionArgs3; args[0] = String.valueOf(contactId); args[1] = accountType; args[2] = accountName; + } else { + query = "SELECT count(_id) FROM " + Tables.RAW_CONTACTS + + " WHERE " + RawContacts.CONTACT_ID + "=?" + + " AND " + RawContacts.ACCOUNT_TYPE + "=?" + + " AND " + RawContacts.ACCOUNT_NAME + "=?" + + " AND " + RawContacts.DATA_SET + "=?"; + args = mSelectionArgs4; + args[0] = String.valueOf(contactId); + args[1] = accountType; + args[2] = accountName; + args[3] = dataSet; } Cursor cursor = db.rawQuery(query, args); try { @@ -1527,6 +1568,7 @@ public class ContactAggregator { + RawContactsColumns.DISPLAY_NAME_SOURCE + "," + RawContacts.ACCOUNT_TYPE + "," + RawContacts.ACCOUNT_NAME + "," + + RawContacts.DATA_SET + "," + RawContacts.SOURCE_ID + "," + RawContacts.CUSTOM_RINGTONE + "," + RawContacts.SEND_TO_VOICEMAIL + "," @@ -1558,17 +1600,18 @@ public class ContactAggregator { int DISPLAY_NAME_SOURCE = 2; int ACCOUNT_TYPE = 3; int ACCOUNT_NAME = 4; - int SOURCE_ID = 5; - int CUSTOM_RINGTONE = 6; - int SEND_TO_VOICEMAIL = 7; - int LAST_TIME_CONTACTED = 8; - int TIMES_CONTACTED = 9; - int STARRED = 10; - int NAME_VERIFIED = 11; - int DATA_ID = 12; - int MIMETYPE_ID = 13; - int IS_SUPER_PRIMARY = 14; - int PHOTO_FILE_ID = 15; + int DATA_SET = 5; + int SOURCE_ID = 6; + int CUSTOM_RINGTONE = 7; + int SEND_TO_VOICEMAIL = 8; + int LAST_TIME_CONTACTED = 9; + int TIMES_CONTACTED = 10; + int STARRED = 11; + int NAME_VERIFIED = 12; + int DATA_ID = 13; + int MIMETYPE_ID = 14; + int IS_SUPER_PRIMARY = 15; + int PHOTO_FILE_ID = 16; } private interface ContactReplaceSqlStatement { @@ -1663,14 +1706,20 @@ public class ContactAggregator { currentRawContactId = rawContactId; totalRowCount++; + // Assemble sub-account. + String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE); + String dataSet = c.getString(RawContactsQuery.DATA_SET); + String accountWithDataSet = (!TextUtils.isEmpty(dataSet)) + ? accountType + "/" + dataSet + : accountType; + // Display name String displayName = c.getString(RawContactsQuery.DISPLAY_NAME); int displayNameSource = c.getInt(RawContactsQuery.DISPLAY_NAME_SOURCE); int nameVerified = c.getInt(RawContactsQuery.NAME_VERIFIED); - String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE); - processDisplayNameCanditate(rawContactId, displayName, displayNameSource, - mContactsProvider.isWritableAccount(accountType), nameVerified != 0); - + processDisplayNameCandidate(rawContactId, displayName, displayNameSource, + mContactsProvider.isWritableAccountWithDataSet(accountWithDataSet), + nameVerified != 0); // Contact options if (!c.isNull(RawContactsQuery.SEND_TO_VOICEMAIL)) { @@ -1701,7 +1750,7 @@ public class ContactAggregator { } ContactLookupKey.appendToLookupKey(mSb, - c.getString(RawContactsQuery.ACCOUNT_TYPE), + accountWithDataSet, c.getString(RawContactsQuery.ACCOUNT_NAME), rawContactId, c.getString(RawContactsQuery.SOURCE_ID), @@ -1715,7 +1764,9 @@ public class ContactAggregator { boolean superPrimary = c.getInt(RawContactsQuery.IS_SUPER_PRIMARY) != 0; if (mimetypeId == mMimeTypeIdPhoto) { if (!foundSuperPrimaryPhoto) { - // Lookup the metadata for the photo, if available. + // Lookup the metadata for the photo, if available. Note that data set + // does not come into play here, since accounts are looked up in the + // account manager in the priority resolver. PhotoEntry photoEntry = getPhotoMetadata(db, photoFileId); String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE); int priority = mPhotoPriorityResolver.getPhotoPriority(accountType); @@ -1773,7 +1824,7 @@ public class ContactAggregator { * for the aggregate contact currently evaluated. If so, it updates * {@link #mDisplayNameCandidate} with the new values. */ - private void processDisplayNameCanditate(long rawContactId, String displayName, + private void processDisplayNameCandidate(long rawContactId, String displayName, int displayNameSource, boolean writableAccount, boolean verified) { boolean replace = false; @@ -1853,6 +1904,9 @@ public class ContactAggregator { long photoFileId = c.getLong(PhotoIdQuery.PHOTO_FILE_ID); boolean superPrimary = c.getInt(PhotoIdQuery.IS_SUPER_PRIMARY) != 0; PhotoEntry photoEntry = getPhotoMetadata(db, photoFileId); + + // Note that data set does not come into play here, since accounts are looked up in + // the account manager in the priority resolver. String accountType = c.getString(PhotoIdQuery.ACCOUNT_TYPE); int priority = mPhotoPriorityResolver.getPhotoPriority(accountType); if (superPrimary || hasHigherPhotoPriority( @@ -1956,6 +2010,8 @@ public class ContactAggregator { RawContacts.NAME_VERIFIED, RawContacts.SOURCE_ID, RawContacts.ACCOUNT_TYPE, + RawContacts.DATA_SET, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, }; int _ID = 0; @@ -1964,6 +2020,8 @@ public class ContactAggregator { int NAME_VERIFIED = 3; int SOURCE_ID = 4; int ACCOUNT_TYPE = 5; + int DATA_SET = 6; + int ACCOUNT_TYPE_AND_DATA_SET = 7; } public void updateDisplayNameForRawContact(SQLiteDatabase db, long rawContactId) { @@ -1981,7 +2039,7 @@ public class ContactAggregator { mDisplayNameCandidate.clear(); mSelectionArgs1[0] = String.valueOf(contactId); - final Cursor c = db.query(Tables.RAW_CONTACTS, DisplayNameQuery.COLUMNS, + final Cursor c = db.query(Views.RAW_CONTACTS, DisplayNameQuery.COLUMNS, RawContacts.CONTACT_ID + "=?", mSelectionArgs1, null, null, null); try { while (c.moveToNext()) { @@ -1989,10 +2047,11 @@ public class ContactAggregator { String displayName = c.getString(DisplayNameQuery.DISPLAY_NAME); int displayNameSource = c.getInt(DisplayNameQuery.DISPLAY_NAME_SOURCE); int nameVerified = c.getInt(DisplayNameQuery.NAME_VERIFIED); - String accountType = c.getString(DisplayNameQuery.ACCOUNT_TYPE); - - processDisplayNameCanditate(rawContactId, displayName, displayNameSource, - mContactsProvider.isWritableAccount(accountType), nameVerified != 0); + String accountTypeAndDataSet = c.getString( + DisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET); + processDisplayNameCandidate(rawContactId, displayName, displayNameSource, + mContactsProvider.isWritableAccountWithDataSet(accountTypeAndDataSet), + nameVerified != 0); // If the raw contact has no source id, the lookup key is based on the display // name, so the lookup key needs to be updated. @@ -2035,14 +2094,14 @@ public class ContactAggregator { String[] COLUMNS = new String[] { RawContacts._ID, RawContactsColumns.DISPLAY_NAME, - RawContacts.ACCOUNT_TYPE, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.ACCOUNT_NAME, RawContacts.SOURCE_ID, }; int ID = 0; int DISPLAY_NAME = 1; - int ACCOUNT_TYPE = 2; + int ACCOUNT_TYPE_AND_DATA_SET = 2; int ACCOUNT_NAME = 3; int SOURCE_ID = 4; } @@ -2059,12 +2118,12 @@ public class ContactAggregator { public void updateLookupKeyForContact(SQLiteDatabase db, long contactId) { mSb.setLength(0); mSelectionArgs1[0] = String.valueOf(contactId); - final Cursor c = db.query(Tables.RAW_CONTACTS, LookupKeyQuery.COLUMNS, + final Cursor c = db.query(Views.RAW_CONTACTS, LookupKeyQuery.COLUMNS, RawContacts.CONTACT_ID + "=?", mSelectionArgs1, null, null, RawContacts._ID); try { while (c.moveToNext()) { ContactLookupKey.appendToLookupKey(mSb, - c.getString(LookupKeyQuery.ACCOUNT_TYPE), + c.getString(LookupKeyQuery.ACCOUNT_TYPE_AND_DATA_SET), c.getString(LookupKeyQuery.ACCOUNT_NAME), c.getLong(LookupKeyQuery.ID), c.getString(LookupKeyQuery.SOURCE_ID), diff --git a/src/com/android/providers/contacts/ContactDirectoryManager.java b/src/com/android/providers/contacts/ContactDirectoryManager.java index f2c8422..b9629e6 100644 --- a/src/com/android/providers/contacts/ContactDirectoryManager.java +++ b/src/com/android/providers/contacts/ContactDirectoryManager.java @@ -44,6 +44,7 @@ import java.util.List; /** * Manages the contents of the {@link Directory} table. */ +// TODO: Determine whether directories need to be aware of data sets under the account. public class ContactDirectoryManager { private static final String TAG = "ContactDirectoryManager"; diff --git a/src/com/android/providers/contacts/ContactLookupKey.java b/src/com/android/providers/contacts/ContactLookupKey.java index 0590bfb..c35e2b2 100644 --- a/src/com/android/providers/contacts/ContactLookupKey.java +++ b/src/com/android/providers/contacts/ContactLookupKey.java @@ -52,16 +52,17 @@ public class ContactLookupKey { * Returns a short hash code that functions as an additional precaution against the exceedingly * improbable collision between sync IDs in different accounts. */ - public static int getAccountHashCode(String accountType, String accountName) { - if (accountType == null || accountName == null) { + public static int getAccountHashCode(String accountTypeWithDataSet, String accountName) { + if (accountTypeWithDataSet == null || accountName == null) { return 0; } - return (accountType.hashCode() ^ accountName.hashCode()) & 0xFFF; + return (accountTypeWithDataSet.hashCode() ^ accountName.hashCode()) & 0xFFF; } - public static void appendToLookupKey(StringBuilder lookupKey, String accountType, - String accountName, long rawContactId, String sourceId, String displayName) { + public static void appendToLookupKey(StringBuilder lookupKey, String accountTypeWidhtDataSet, + String accountName, long rawContactId, String sourceId, + String displayName) { if (displayName == null) { displayName = ""; } @@ -70,7 +71,7 @@ public class ContactLookupKey { lookupKey.append("."); } - lookupKey.append(getAccountHashCode(accountType, accountName)); + lookupKey.append(getAccountHashCode(accountTypeWidhtDataSet, accountName)); if (sourceId == null) { lookupKey.append('r').append(rawContactId).append('-').append( NameNormalizer.normalize(displayName)); diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java index a183a8c..68a0aab 100644 --- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java +++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java @@ -102,7 +102,7 @@ import java.util.Locale; * 600-699 Ice Cream Sandwich * </pre> */ - static final int DATABASE_VERSION = 610; + static final int DATABASE_VERSION = 611; private static final String DATABASE_NAME = "contacts2.db"; private static final String DATABASE_PRESENCE = "presence_db"; @@ -235,7 +235,8 @@ import java.util.Locale; + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "," + RawContacts.CONTACT_ID; final String RAW_CONTACT_IS_LOCAL = RawContactsColumns.CONCRETE_ACCOUNT_NAME - + " IS NULL AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL"; + + " IS NULL AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL AND " + + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL"; final String ZERO_GROUP_MEMBERSHIPS = "COUNT(" + GroupsColumns.CONCRETE_ID + ")=0"; @@ -259,7 +260,12 @@ import java.util.Locale; " GROUP BY " + RawContacts.CONTACT_ID; final String GROUP_HAS_ACCOUNT_AND_SOURCE_ID = Groups.SOURCE_ID + "=? AND " - + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?"; + + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND " + + Groups.DATA_SET + " IS NULL"; + + final String GROUP_HAS_ACCOUNT_AND_DATA_SET_AND_SOURCE_ID = Groups.SOURCE_ID + "=? AND " + + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND " + + Groups.DATA_SET + "=?"; public static final String CONTACT_VISIBLE = "EXISTS (SELECT _id FROM " + Tables.VISIBLE_CONTACTS @@ -294,6 +300,10 @@ import java.util.Locale; Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME; public static final String CONCRETE_ACCOUNT_TYPE = Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE; + public static final String CONCRETE_DATA_SET = + Tables.RAW_CONTACTS + "." + RawContacts.DATA_SET; + public static final String CONCRETE_ACCOUNT_TYPE_AND_DATA_SET = + Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE_AND_DATA_SET; public static final String CONCRETE_SOURCE_ID = Tables.RAW_CONTACTS + "." + RawContacts.SOURCE_ID; public static final String CONCRETE_VERSION = @@ -388,6 +398,9 @@ import java.util.Locale; Tables.GROUPS + "." + Groups.ACCOUNT_NAME; public static final String CONCRETE_ACCOUNT_TYPE = Tables.GROUPS + "." + Groups.ACCOUNT_TYPE; + public static final String CONCRETE_DATA_SET = Tables.GROUPS + "." + Groups.DATA_SET; + public static final String CONCRETE_ACCOUNT_TYPE_AND_DATA_SET = Tables.GROUPS + "." + + Groups.ACCOUNT_TYPE_AND_DATA_SET; public static final String CONCRETE_ACTION = Tables.GROUPS + "." + Groups.ACTION; public static final String CONCRETE_ACTION_URI = Tables.GROUPS + "." + Groups.ACTION_URI; } @@ -538,6 +551,7 @@ import java.util.Locale; public interface AccountsColumns { String ACCOUNT_NAME = RawContacts.ACCOUNT_NAME; String ACCOUNT_TYPE = RawContacts.ACCOUNT_TYPE; + String DATA_SET = RawContacts.DATA_SET; String PROFILE_RAW_CONTACT_ID = "profile_raw_contact_id"; } @@ -847,6 +861,7 @@ import java.util.Locale; RawContacts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + RawContacts.ACCOUNT_NAME + " STRING DEFAULT NULL, " + RawContacts.ACCOUNT_TYPE + " STRING DEFAULT NULL, " + + RawContacts.DATA_SET + " STRING DEFAULT NULL, " + RawContacts.SOURCE_ID + " TEXT," + RawContacts.RAW_CONTACT_IS_READ_ONLY + " INTEGER NOT NULL DEFAULT 0," + RawContacts.VERSION + " INTEGER NOT NULL DEFAULT 1," + @@ -888,6 +903,14 @@ import java.util.Locale; RawContacts.ACCOUNT_NAME + ");"); + db.execSQL("CREATE INDEX raw_contacts_source_id_data_set_index ON " + + Tables.RAW_CONTACTS + " (" + + RawContacts.SOURCE_ID + ", " + + RawContacts.ACCOUNT_TYPE + ", " + + RawContacts.ACCOUNT_NAME + ", " + + RawContacts.DATA_SET + + ");"); + db.execSQL("CREATE TABLE " + Tables.STREAM_ITEMS + " (" + StreamItems._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + StreamItems.RAW_CONTACT_ID + " INTEGER NOT NULL, " + @@ -1042,6 +1065,7 @@ import java.util.Locale; GroupsColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + Groups.ACCOUNT_NAME + " STRING DEFAULT NULL, " + Groups.ACCOUNT_TYPE + " STRING DEFAULT NULL, " + + Groups.DATA_SET + " STRING DEFAULT NULL, " + Groups.SOURCE_ID + " TEXT," + Groups.VERSION + " INTEGER NOT NULL DEFAULT 1," + Groups.DIRTY + " INTEGER NOT NULL DEFAULT 0," + @@ -1069,6 +1093,13 @@ import java.util.Locale; Groups.ACCOUNT_NAME + ");"); + db.execSQL("CREATE INDEX groups_source_id_data_set_index ON " + Tables.GROUPS + " (" + + Groups.SOURCE_ID + ", " + + Groups.ACCOUNT_TYPE + ", " + + Groups.ACCOUNT_NAME + ", " + + Groups.DATA_SET + + ");"); + db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.AGGREGATION_EXCEPTIONS + " (" + AggregationExceptionColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + AggregationExceptions.TYPE + " INTEGER NOT NULL, " + @@ -1175,6 +1206,7 @@ import java.util.Locale; db.execSQL("CREATE TABLE " + Tables.ACCOUNTS + " (" + AccountsColumns.ACCOUNT_NAME + " TEXT, " + AccountsColumns.ACCOUNT_TYPE + " TEXT, " + + AccountsColumns.DATA_SET + " TEXT, " + AccountsColumns.PROFILE_RAW_CONTACT_ID + " INTEGER" + ");"); @@ -1185,9 +1217,7 @@ import java.util.Locale; // Allow contacts without any account to be created for now. Achieve that // by inserting a fake account with both type and name as NULL. - // This "account" should be eliminated as soon as the first real writable account - // is added to the phone. - db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " VALUES(NULL, NULL, NULL)"); + db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " VALUES(NULL, NULL, NULL, NULL)"); createDirectoriesTable(db); createSearchIndexTable(db); @@ -1415,8 +1445,15 @@ import java.util.Locale; String syncColumns = RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AS " + RawContacts.ACCOUNT_NAME + "," + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + RawContacts.ACCOUNT_TYPE + "," + + RawContactsColumns.CONCRETE_DATA_SET + " AS " + RawContacts.DATA_SET + "," + + "(CASE WHEN " + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL THEN " + + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + + " ELSE " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "||'/'||" + + RawContactsColumns.CONCRETE_DATA_SET + " END) AS " + + RawContacts.ACCOUNT_TYPE_AND_DATA_SET + "," + RawContactsColumns.CONCRETE_SOURCE_ID + " AS " + RawContacts.SOURCE_ID + "," - + RawContactsColumns.CONCRETE_NAME_VERIFIED + " AS " + RawContacts.NAME_VERIFIED + "," + + RawContactsColumns.CONCRETE_NAME_VERIFIED + " AS " + + RawContacts.NAME_VERIFIED + "," + RawContactsColumns.CONCRETE_VERSION + " AS " + RawContacts.VERSION + "," + RawContactsColumns.CONCRETE_DIRTY + " AS " + RawContacts.DIRTY + "," + RawContactsColumns.CONCRETE_SYNC1 + " AS " + RawContacts.SYNC1 + "," @@ -1682,6 +1719,10 @@ import java.util.Locale; String groupsColumns = Groups.ACCOUNT_NAME + "," + Groups.ACCOUNT_TYPE + "," + + Groups.DATA_SET + "," + + "(CASE WHEN " + Groups.DATA_SET + " IS NULL THEN " + Groups.ACCOUNT_TYPE + + " ELSE " + Groups.ACCOUNT_TYPE + "||" + Groups.DATA_SET + " END) AS " + + Groups.ACCOUNT_TYPE_AND_DATA_SET + "," + Groups.SOURCE_ID + "," + Groups.VERSION + "," + Groups.DIRTY + "," @@ -2063,6 +2104,11 @@ import java.util.Locale; oldVersion = 610; } + if (oldVersion < 611) { + upgradeViewsAndTriggers = true; + upgradeToVersion611(db); + oldVersion = 611; + } if (upgradeViewsAndTriggers) { createContactsViews(db); @@ -3221,6 +3267,18 @@ import java.util.Locale; db.execSQL("ALTER TABLE calls ADD is_read INTEGER;"); } + private void upgradeToVersion611(SQLiteDatabase db) { + db.execSQL("ALTER TABLE raw_contacts ADD data_set TEXT DEFAULT NULL;"); + db.execSQL("ALTER TABLE groups ADD data_set TEXT DEFAULT NULL;"); + db.execSQL("ALTER TABLE accounts ADD data_set TEXT DEFAULT NULL;"); + + db.execSQL("CREATE INDEX raw_contacts_source_id_data_set_index ON raw_contacts " + + "(sourceid, account_type, account_name, data_set);"); + + db.execSQL("CREATE INDEX groups_source_id_data_set_index ON groups " + + "(sourceid, account_type, account_name, data_set);"); + } + public String extractHandleFromEmailAddress(String email) { Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email); if (tokens.length == 0) { @@ -3348,7 +3406,7 @@ import java.util.Locale; SQLiteDatabase db = getWritableDatabase(); db.execSQL("DELETE FROM " + Tables.ACCOUNTS + ";"); - db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " VALUES(NULL, NULL, NULL)"); + db.execSQL("INSERT INTO " + Tables.ACCOUNTS + " VALUES(NULL, NULL, NULL, NULL)"); db.execSQL("DELETE FROM " + Tables.CONTACTS + ";"); db.execSQL("DELETE FROM " + Tables.RAW_CONTACTS + ";"); @@ -3594,6 +3652,10 @@ import java.util.Locale; + GroupsColumns.CONCRETE_ACCOUNT_NAME + " AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " = " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + + " AND (" + RawContactsColumns.CONCRETE_DATA_SET + " = " + + GroupsColumns.CONCRETE_DATA_SET + + " OR " + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL AND " + + GroupsColumns.CONCRETE_DATA_SET + " IS NULL)" + " AND " + Groups.AUTO_ADD + " != 0" + ")" + ") OR EXISTS (" + @@ -3602,6 +3664,7 @@ import java.util.Locale; " WHERE " + RawContacts.CONTACT_ID + "=?" + " AND " + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " IS NULL " + " AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL" + + " AND " + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL" + ")", new String[] { contactIdAsString, diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 40167c4..eb85561 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -142,10 +142,7 @@ import android.util.Log; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -349,7 +346,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE - + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND (" + + GroupsColumns.CONCRETE_DATA_SET + + "=" + RawContactsColumns.CONCRETE_DATA_SET + " OR " + + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND " + + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)" + " AND " + Groups.FAVORITES + " != 0"; private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID = @@ -357,8 +358,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "=" - + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND " - + Groups.AUTO_ADD + " != 0"; + + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND (" + + GroupsColumns.CONCRETE_DATA_SET + "=" + + RawContactsColumns.CONCRETE_DATA_SET + " OR " + + GroupsColumns.CONCRETE_DATA_SET + " IS NULL AND " + + RawContactsColumns.CONCRETE_DATA_SET + " IS NULL)" + + " AND " + Groups.AUTO_ADD + " != 0"; private static final String[] PROJECTION_GROUP_ID = new String[]{Tables.GROUPS + "." + Groups._ID}; @@ -413,6 +418,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun RawContactsColumns.CONCRETE_ID, RawContactsColumns.CONCRETE_ACCOUNT_TYPE, RawContactsColumns.CONCRETE_ACCOUNT_NAME, + RawContactsColumns.CONCRETE_DATA_SET, DataColumns.CONCRETE_ID, ContactsColumns.CONCRETE_ID }; @@ -420,8 +426,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun public static final int RAW_CONTACT_ID = 0; public static final int ACCOUNT_TYPE = 1; public static final int ACCOUNT_NAME = 2; - public static final int DATA_ID = 3; - public static final int CONTACT_ID = 4; + public static final int DATA_SET = 3; + public static final int DATA_ID = 4; + public static final int CONTACT_ID = 5; } interface RawContactsQuery { @@ -431,11 +438,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun RawContacts.DELETED, RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_NAME, + RawContacts.DATA_SET, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, }; int DELETED = 0; int ACCOUNT_TYPE = 1; int ACCOUNT_NAME = 2; + int DATA_SET = 3; + int ACCOUNT_TYPE_AND_DATA_SET = 4; } public static final String DEFAULT_ACCOUNT_TYPE = "com.google"; @@ -517,6 +528,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun Data.NAME_RAW_CONTACT_ID, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, + RawContacts.DATA_SET, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.DIRTY, RawContacts.NAME_VERIFIED, RawContacts.SOURCE_ID, @@ -569,6 +582,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private static final ProjectionMap sRawContactColumns = ProjectionMap.builder() .add(RawContacts.ACCOUNT_NAME) .add(RawContacts.ACCOUNT_TYPE) + .add(RawContacts.DATA_SET) + .add(RawContacts.ACCOUNT_TYPE_AND_DATA_SET) .add(RawContacts.DIRTY) .add(RawContacts.NAME_VERIFIED) .add(RawContacts.SOURCE_ID) @@ -810,6 +825,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun .add(Groups._ID) .add(Groups.ACCOUNT_NAME) .add(Groups.ACCOUNT_TYPE) + .add(Groups.DATA_SET) + .add(Groups.ACCOUNT_TYPE_AND_DATA_SET) .add(Groups.SOURCE_ID) .add(Groups.DIRTY) .add(Groups.VERSION) @@ -1181,12 +1198,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private boolean mDirectoryCacheValid = false; /** - * An entry in group id cache. It maps the combination of (account type, account name + * An entry in group id cache. It maps the combination of (account type, account name, data set, * and source id) to group row id. */ public static class GroupIdCacheEntry { String accountType; String accountName; + String dataSet; String sourceId; long groupId; } @@ -1913,7 +1931,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mContactAggregator.onRawContactInsert(mTransactionContext, mDb, rawContactId); } - Map<Long, Account> insertedProfileRawContactAccountMap = + Map<Long, AccountWithDataSet> insertedProfileRawContactAccountMap = mTransactionContext.getInsertedProfileRawContactIds(); if (!insertedProfileRawContactAccountMap.isEmpty()) { for (long profileRawContactId : insertedProfileRawContactAccountMap.keySet()) { @@ -2239,6 +2257,25 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } /** + * Resolves the account and builds an {@link AccountWithDataSet} based on the data set specified + * in the URI or values (if any). + * @param uri Current {@link Uri} being operated on. + * @param values {@link ContentValues} to read and possibly update. + */ + private AccountWithDataSet resolveAccountWithDataSet(Uri uri, ContentValues values) { + final Account account = resolveAccount(uri, mValues); + AccountWithDataSet accountWithDataSet = null; + if (account != null) { + String dataSet = getQueryParameter(uri, RawContacts.DATA_SET); + if (dataSet == null) { + dataSet = mValues.getAsString(RawContacts.DATA_SET); + } + accountWithDataSet = new AccountWithDataSet(account.name, account.type, dataSet); + } + return accountWithDataSet; + } + + /** * Inserts an item in the contacts table * * @param values the values for the new row @@ -2263,7 +2300,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mValues.putAll(values); mValues.putNull(RawContacts.CONTACT_ID); - final Account account = resolveAccount(uri, mValues); + AccountWithDataSet accountWithDataSet = resolveAccountWithDataSet(uri, mValues); if (values.containsKey(RawContacts.DELETED) && values.getAsInteger(RawContacts.DELETED) != 0) { @@ -2284,10 +2321,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun if (forProfile) { // Trigger creation of the user profile Contact (or association with the existing one) // at the end of the transaction. - mTransactionContext.profileRawContactInserted(rawContactId, account); + mTransactionContext.profileRawContactInserted(rawContactId, accountWithDataSet); } else { // Trigger creation of a Contact based on this RawContact at the end of transaction - mTransactionContext.rawContactInserted(rawContactId, account); + mTransactionContext.rawContactInserted(rawContactId, accountWithDataSet); } if (!callerIsSyncAdapter) { @@ -2759,6 +2796,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mValues.putAll(values); final Account account = resolveAccount(uri, mValues); + String dataSet = null; + if (account != null && mValues.containsKey(Groups.DATA_SET)) { + dataSet = mValues.getAsString(Groups.DATA_SET); + } // Replace package with internal mapping final String packageName = mValues.getAsString(Groups.RES_PACKAGE); @@ -2783,12 +2824,18 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun String[] selectionArgs; if (account == null) { selection = RawContacts.ACCOUNT_NAME + " IS NULL AND " - + RawContacts.ACCOUNT_TYPE + " IS NULL"; + + RawContacts.ACCOUNT_TYPE + " IS NULL AND " + + RawContacts.DATA_SET + " IS NULL"; selectionArgs = null; - } else { + } else if (dataSet == null) { selection = RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?"; selectionArgs = new String[]{account.name, account.type}; + } else { + selection = RawContacts.ACCOUNT_NAME + "=? AND " + + RawContacts.ACCOUNT_TYPE + "=? AND " + + RawContacts.DATA_SET + "=?"; + selectionArgs = new String[]{account.name, account.type, dataSet}; } Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID, RawContacts.STARRED}, @@ -2997,7 +3044,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // updates didn't do any sort of account enforcement, while social stream item // updates do. We can't expect callers of the old API to start passing account // information along, so we just populate the account params appropriately for - // the raw contact. + // the raw contact. Data set is not relevant here, as we only check account + // name and type. if (accountName != null && accountType != null) { streamItemValues.put(RawContacts.ACCOUNT_NAME, accountName); streamItemValues.put(RawContacts.ACCOUNT_TYPE, accountType); @@ -3671,6 +3719,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) { mVisibleTouched = true; } + + // TODO: This will not work for groups that have a data set specified, since the content + // resolver will not be able to request a sync for the right source (unless it is updated + // to key off account with data set). if (updatedValues.containsKey(Groups.SHOULD_SYNC) && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) { Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME, @@ -3747,6 +3799,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun int previousDeleted = 0; String accountType = null; String accountName = null; + String dataSet = null; if (requestUndoDelete) { Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection, mSelectionArgs1, null, null, null); @@ -3755,6 +3808,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun previousDeleted = cursor.getInt(RawContactsQuery.DELETED); accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE); accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME); + dataSet = cursor.getString(RawContactsQuery.DATA_SET); } } finally { cursor.close(); @@ -3813,7 +3867,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } if (requestUndoDelete && previousDeleted == 1) { mTransactionContext.rawContactInserted(rawContactId, - new Account(accountName, accountType)); + new AccountWithDataSet(accountName, accountType, dataSet)); } } return count; @@ -4027,61 +4081,95 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun protected boolean updateAccountsInBackground(Account[] accounts) { // TODO : Check the unit test. boolean accountsChanged = false; - HashSet<Account> existingAccounts = new HashSet<Account>(); mDb = mDbHelper.getWritableDatabase(); mDb.beginTransaction(); try { - findValidAccounts(existingAccounts); + Set<AccountWithDataSet> existingAccountsWithDataSets = + findValidAccountsWithDataSets(Tables.ACCOUNTS); - // Add a row to the ACCOUNTS table for each new account + // Add a row to the ACCOUNTS table (with no data set) for each new account. for (Account account : accounts) { - if (!existingAccounts.contains(account)) { + AccountWithDataSet accountWithDataSet = new AccountWithDataSet( + account.name, account.type, null); + if (!existingAccountsWithDataSets.contains(accountWithDataSet)) { accountsChanged = true; + + // Add an account entry with an empty data set to match the account. mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME - + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)", - new String[] {account.name, account.type}); + + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET + + ") VALUES (?, ?, ?)", + new String[] { + accountWithDataSet.getAccountName(), + accountWithDataSet.getAccountType(), + accountWithDataSet.getDataSet() + }); } } - // Remove all valid accounts from the existing account set. What is left - // in the accountsToDelete set will be extra accounts whose data must be deleted. - HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts); - for (Account account : accounts) { - accountsToDelete.remove(account); + // Check each of the existing sub-accounts against the account list. If the owning + // account no longer exists, the sub-account and all its data should be deleted. + List<AccountWithDataSet> accountsWithDataSetsToDelete = + new ArrayList<AccountWithDataSet>(); + List<Account> accountList = Arrays.asList(accounts); + for (AccountWithDataSet accountWithDataSet : existingAccountsWithDataSets) { + Account owningAccount = new Account( + accountWithDataSet.getAccountName(), accountWithDataSet.getAccountType()); + if (!accountList.contains(owningAccount)) { + accountsWithDataSetsToDelete.add(accountWithDataSet); + } } - if (!accountsToDelete.isEmpty()) { + if (!accountsWithDataSetsToDelete.isEmpty()) { accountsChanged = true; - for (Account account : accountsToDelete) { - Log.d(TAG, "removing data for removed account " + account); - String[] params = new String[] {account.name, account.type}; + for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) { + Log.d(TAG, "removing data for removed account " + accountWithDataSet); + String[] accountParams = new String[] { + accountWithDataSet.getAccountName(), + accountWithDataSet.getAccountType() + }; + String[] accountWithDataSetParams = accountWithDataSet.getDataSet() == null + ? accountParams + : new String[] { + accountWithDataSet.getAccountName(), + accountWithDataSet.getAccountType(), + accountWithDataSet.getDataSet() + }; + String groupsDataSetClause = " AND " + Groups.DATA_SET + + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?"); + String rawContactsDataSetClause = " AND " + RawContacts.DATA_SET + + (accountWithDataSet.getDataSet() == null ? " IS NULL" : " = ?"); + mDb.execSQL( "DELETE FROM " + Tables.GROUPS + " WHERE " + Groups.ACCOUNT_NAME + " = ?" + - " AND " + Groups.ACCOUNT_TYPE + " = ?", params); + " AND " + Groups.ACCOUNT_TYPE + " = ?" + + groupsDataSetClause, accountWithDataSetParams); mDb.execSQL( "DELETE FROM " + Tables.PRESENCE + " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" + "SELECT " + RawContacts._ID + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" + - " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params); + " AND " + RawContacts.ACCOUNT_TYPE + " = ?" + + rawContactsDataSetClause + ")", accountWithDataSetParams); mDb.execSQL( "DELETE FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" + - " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params); + " AND " + RawContacts.ACCOUNT_TYPE + " = ?" + + rawContactsDataSetClause, accountWithDataSetParams); mDb.execSQL( "DELETE FROM " + Tables.SETTINGS + " WHERE " + Settings.ACCOUNT_NAME + " = ?" + - " AND " + Settings.ACCOUNT_TYPE + " = ?", params); + " AND " + Settings.ACCOUNT_TYPE + " = ?", accountParams); mDb.execSQL( "DELETE FROM " + Tables.ACCOUNTS + " WHERE " + RawContacts.ACCOUNT_NAME + "=?" + - " AND " + RawContacts.ACCOUNT_TYPE + "=?", params); + " AND " + RawContacts.ACCOUNT_TYPE + "=?" + + rawContactsDataSetClause, accountWithDataSetParams); mDb.execSQL( "DELETE FROM " + Tables.DIRECTORIES + " WHERE " + Directory.ACCOUNT_NAME + "=?" + - " AND " + Directory.ACCOUNT_TYPE + "=?", params); + " AND " + Directory.ACCOUNT_TYPE + "=?", accountParams); resetDirectoryCache(); } @@ -4114,7 +4202,31 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun updateSearchIndexInTransaction(); } + // Now that we've done the account-based additions and subtractions from the Accounts + // table, check for raw contacts that have been added with a data set and add Accounts + // entries for those if necessary. + existingAccountsWithDataSets = findValidAccountsWithDataSets(Tables.ACCOUNTS); + Set<AccountWithDataSet> rawContactAccountsWithDataSets = + findValidAccountsWithDataSets(Tables.RAW_CONTACTS); + rawContactAccountsWithDataSets.removeAll(existingAccountsWithDataSets); + + // Any remaining raw contact sub-accounts need to be added to the Accounts table. + for (AccountWithDataSet accountWithDataSet : rawContactAccountsWithDataSets) { + accountsChanged = true; + + // Add an account entry to match the raw contact. + mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME + + ", " + RawContacts.ACCOUNT_TYPE + ", " + RawContacts.DATA_SET + + ") VALUES (?, ?, ?)", + new String[] { + accountWithDataSet.getAccountName(), + accountWithDataSet.getAccountType(), + accountWithDataSet.getDataSet() + }); + } + if (accountsChanged) { + // TODO: Should sync state take data set into consideration? mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); } mDb.setTransactionSuccessful(); @@ -4156,21 +4268,25 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } /** - * Finds all distinct accounts present in the specified table. + * Finds all distinct account types and data sets present in the specified table. */ - private void findValidAccounts(Set<Account> validAccounts) { + private Set<AccountWithDataSet> findValidAccountsWithDataSets(String table) { + Set<AccountWithDataSet> accountsWithDataSets = new HashSet<AccountWithDataSet>(); Cursor c = mDb.rawQuery( - "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE + - " FROM " + Tables.ACCOUNTS, null); + "SELECT DISTINCT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE + + "," + RawContacts.DATA_SET + + " FROM " + table, null); try { while (c.moveToNext()) { if (!c.isNull(0) || !c.isNull(1)) { - validAccounts.add(new Account(c.getString(0), c.getString(1))); + accountsWithDataSets.add( + new AccountWithDataSet(c.getString(0), c.getString(1), c.getString(2))); } } } finally { c.close(); } + return accountsWithDataSets; } @Override @@ -5086,7 +5202,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun case GROUPS: { qb.setTables(Views.GROUPS); qb.setProjectionMap(sGroupsProjectionMap); - appendAccountFromParameter(qb, uri); + appendAccountFromParameter(qb, uri, true); break; } @@ -5106,7 +5222,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun qb.setProjectionMap(returnGroupCountPerAccount ? sGroupsSummaryProjectionMapWithGroupCountPerAccount : sGroupsSummaryProjectionMap); - appendAccountFromParameter(qb, uri); + appendAccountFromParameter(qb, uri, true); groupBy = GroupsColumns.CONCRETE_ID; break; } @@ -5155,7 +5271,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun case SETTINGS: { qb.setTables(Tables.SETTINGS); qb.setProjectionMap(sSettingsProjectionMap); - appendAccountFromParameter(qb, uri); + appendAccountFromParameter(qb, uri, false); // When requesting specific columns, this query requires // late-binding of the GroupMembership MIME-type. @@ -5503,17 +5619,17 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } private interface LookupBySourceIdQuery { - String TABLE = Tables.RAW_CONTACTS; + String TABLE = Views.RAW_CONTACTS; String COLUMNS[] = { RawContacts.CONTACT_ID, - RawContacts.ACCOUNT_TYPE, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.ACCOUNT_NAME, RawContacts.SOURCE_ID }; int CONTACT_ID = 0; - int ACCOUNT_TYPE = 1; + int ACCOUNT_TYPE_AND_DATA_SET = 1; int ACCOUNT_NAME = 2; int SOURCE_ID = 3; } @@ -5536,10 +5652,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun sb.toString(), null, null, null, null); try { while (c.moveToNext()) { - String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE); + String accountTypeAndDataSet = + c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE_AND_DATA_SET); String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME); int accountHashCode = - ContactLookupKey.getAccountHashCode(accountType, accountName); + ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName); String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID); for (int i = 0; i < segments.size(); i++) { LookupKeySegment segment = segments.get(i); @@ -5559,17 +5676,17 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } private interface LookupByRawContactIdQuery { - String TABLE = Tables.RAW_CONTACTS; + String TABLE = Views.RAW_CONTACTS; String COLUMNS[] = { RawContacts.CONTACT_ID, - RawContacts.ACCOUNT_TYPE, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.ACCOUNT_NAME, RawContacts._ID, }; int CONTACT_ID = 0; - int ACCOUNT_TYPE = 1; + int ACCOUNT_TYPE_AND_DATA_SET = 1; int ACCOUNT_NAME = 2; int ID = 3; } @@ -5592,10 +5709,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun sb.toString(), null, null, null, null); try { while (c.moveToNext()) { - String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE); + String accountTypeAndDataSet = c.getString( + LookupByRawContactIdQuery.ACCOUNT_TYPE_AND_DATA_SET); String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME); int accountHashCode = - ContactLookupKey.getAccountHashCode(accountType, accountName); + ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName); String rawContactId = c.getString(LookupByRawContactIdQuery.ID); for (int i = 0; i < segments.size(); i++) { LookupKeySegment segment = segments.get(i); @@ -5619,13 +5737,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun String COLUMNS[] = { RawContacts.CONTACT_ID, - RawContacts.ACCOUNT_TYPE, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.ACCOUNT_NAME, NameLookupColumns.NORMALIZED_NAME }; int CONTACT_ID = 0; - int ACCOUNT_TYPE = 1; + int ACCOUNT_TYPE_AND_DATA_SET = 1; int ACCOUNT_NAME = 2; int NORMALIZED_NAME = 3; } @@ -5650,10 +5768,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun sb.toString(), null, null, null, null); try { while (c.moveToNext()) { - String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE); + String accountTypeAndDataSet = + c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET); String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME); int accountHashCode = - ContactLookupKey.getAccountHashCode(accountType, accountName); + ContactLookupKey.getAccountHashCode(accountTypeAndDataSet, accountName); String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME); for (int i = 0; i < segments.size(); i++) { LookupKeySegment segment = segments.get(i); @@ -5951,13 +6070,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun sb.append(Views.RAW_CONTACTS); qb.setTables(sb.toString()); qb.setProjectionMap(sRawContactsProjectionMap); - appendAccountFromParameter(qb, uri); + appendAccountFromParameter(qb, uri, true); } private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) { qb.setTables(Views.RAW_ENTITIES); qb.setProjectionMap(sRawEntityProjectionMap); - appendAccountFromParameter(qb, uri); + appendAccountFromParameter(qb, uri, true); } private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri, @@ -5990,7 +6109,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS); qb.setDistinct(useDistinct); qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap); - appendAccountFromParameter(qb, uri); + appendAccountFromParameter(qb, uri, true); } private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb, @@ -6038,7 +6157,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun qb.setTables(sb.toString()); qb.setProjectionMap(sEntityProjectionMap); - appendAccountFromParameter(qb, uri); + appendAccountFromParameter(qb, uri, true); } private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection, @@ -6138,9 +6257,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return profileRequested; } - private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) { + private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri, + boolean includeDataSet) { final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME); final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE); + final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET); final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType); if (partialUri) { @@ -6153,10 +6274,19 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // already ruled out partial accounts. final boolean validAccount = !TextUtils.isEmpty(accountName); if (validAccount) { - qb.appendWhere(RawContacts.ACCOUNT_NAME + "=" + String toAppend = RawContacts.ACCOUNT_NAME + "=" + DatabaseUtils.sqlEscapeString(accountName) + " AND " + RawContacts.ACCOUNT_TYPE + "=" - + DatabaseUtils.sqlEscapeString(accountType)); + + DatabaseUtils.sqlEscapeString(accountType); + if (includeDataSet) { + if (dataSet == null) { + toAppend += " AND " + RawContacts.DATA_SET + " IS NULL"; + } else { + toAppend += " AND " + RawContacts.DATA_SET + "=" + + DatabaseUtils.sqlEscapeString(dataSet); + } + } + qb.appendWhere(toAppend); } else { qb.appendWhere("1"); } @@ -6165,6 +6295,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private String appendAccountToSelection(Uri uri, String selection) { final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME); final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE); + final String dataSet = getQueryParameter(uri, RawContacts.DATA_SET); final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType); if (partialUri) { @@ -6181,6 +6312,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun + DatabaseUtils.sqlEscapeString(accountName) + " AND " + RawContacts.ACCOUNT_TYPE + "=" + DatabaseUtils.sqlEscapeString(accountType)); + if (!TextUtils.isEmpty(dataSet)) { + selectionSb.append(" AND " + RawContacts.DATA_SET + "=") + .append(DatabaseUtils.sqlEscapeString(dataSet)); + } if (!TextUtils.isEmpty(selection)) { selectionSb.append(" AND ("); selectionSb.append(selection); @@ -6886,23 +7021,24 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } /** - * Returns true if the specified account type is writable. + * Returns true if the specified account type and data set is writable. */ - protected boolean isWritableAccount(String accountType) { - if (accountType == null) { + protected boolean isWritableAccountWithDataSet(String accountTypeAndDataSet) { + if (accountTypeAndDataSet == null) { return true; } - Boolean writable = mAccountWritability.get(accountType); + Boolean writable = mAccountWritability.get(accountTypeAndDataSet); if (writable != null) { return writable; } IContentService contentService = ContentResolver.getContentService(); try { + // TODO(dsantoro): Need to update this logic to allow for sub-accounts. for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) { if (ContactsContract.AUTHORITY.equals(sync.authority) && - accountType.equals(sync.accountType)) { + accountTypeAndDataSet.equals(sync.accountType)) { writable = sync.supportsUploading(); break; } @@ -6915,7 +7051,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun writable = false; } - mAccountWritability.put(accountType, writable); + mAccountWritability.put(accountTypeAndDataSet, writable); return writable; } @@ -7025,7 +7161,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun "r1." + RawContacts._ID + "!=r2." + RawContacts._ID + " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID + " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME + - " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE, + " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE + + " AND r1." + RawContacts.DATA_SET + "=r2." + RawContacts.DATA_SET, null, null, null, null, null); try { while (cursor.moveToNext()) { diff --git a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java index de2e3d4..1b7e475 100644 --- a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java +++ b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java @@ -15,13 +15,13 @@ */ package com.android.providers.contacts; +import com.android.internal.util.Objects; import com.android.providers.contacts.ContactsDatabaseHelper.Clauses; import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns; import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import com.android.providers.contacts.ContactsProvider2.GroupIdCacheEntry; -import android.accounts.Account; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -47,11 +47,13 @@ public class DataRowHandlerForGroupMembership extends DataRowHandler { RawContacts.DELETED, RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_NAME, + RawContacts.DATA_SET, }; int DELETED = 0; int ACCOUNT_TYPE = 1; int ACCOUNT_NAME = 2; + int DATA_SET = 3; } private static final String SELECTION_RAW_CONTACT_ID = RawContacts._ID + "=?"; @@ -168,7 +170,7 @@ public class DataRowHandlerForGroupMembership extends DataRowHandler { if (containsGroupSourceId) { final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID); final long groupId = getOrMakeGroup(db, rawContactId, sourceId, - txContext.getAccountForRawContact(rawContactId)); + txContext.getAccountWithDataSetForRawContact(rawContactId)); values.remove(GroupMembership.GROUP_SOURCE_ID); values.put(GroupMembership.GROUP_ROW_ID, groupId); } @@ -185,9 +187,9 @@ public class DataRowHandlerForGroupMembership extends DataRowHandler { * @throws IllegalStateException if a group needs to be created but the creation failed */ private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId, - Account account) { + AccountWithDataSet accountWithDataSet) { - if (account == null) { + if (accountWithDataSet == null) { mSelectionArgs1[0] = String.valueOf(rawContactId); Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, RawContacts._ID + "=?", mSelectionArgs1, null, null, null); @@ -195,8 +197,10 @@ public class DataRowHandlerForGroupMembership extends DataRowHandler { if (c.moveToFirst()) { String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME); String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE); + String dataSet = c.getString(RawContactsQuery.DATA_SET); if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { - account = new Account(accountName, accountType); + accountWithDataSet = new AccountWithDataSet( + accountName, accountType, dataSet); } } } finally { @@ -204,7 +208,7 @@ public class DataRowHandlerForGroupMembership extends DataRowHandler { } } - if (account == null) { + if (accountWithDataSet == null) { throw new IllegalArgumentException("if the groupmembership only " + "has a sourceid the the contact must be associated with " + "an account"); @@ -219,29 +223,49 @@ public class DataRowHandlerForGroupMembership extends DataRowHandler { int count = entries.size(); for (int i = 0; i < count; i++) { GroupIdCacheEntry entry = entries.get(i); - if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) { + if (entry.accountName.equals(accountWithDataSet.getAccountName()) + && entry.accountType.equals(accountWithDataSet.getAccountType()) + && Objects.equal(entry.dataSet, accountWithDataSet.getDataSet())) { return entry.groupId; } } GroupIdCacheEntry entry = new GroupIdCacheEntry(); - entry.accountName = account.name; - entry.accountType = account.type; + entry.accountName = accountWithDataSet.getAccountName(); + entry.accountType = accountWithDataSet.getAccountType(); + entry.dataSet = accountWithDataSet.getDataSet(); entry.sourceId = sourceId; entries.add(0, entry); - // look up the group that contains this sourceId and has the same account name and type - // as the contact refered to by rawContactId - Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID}, + // look up the group that contains this sourceId and has the same account name, type, and + // data set as the contact refered to by rawContactId + Cursor c; + if (accountWithDataSet.getDataSet() == null) { + c = db.query(Tables.GROUPS, new String[]{RawContacts._ID}, Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID, - new String[]{sourceId, account.name, account.type}, null, null, null); + new String[]{ + sourceId, + accountWithDataSet.getAccountName(), + accountWithDataSet.getAccountType() + }, null, null, null); + } else { + c = db.query(Tables.GROUPS, new String[]{RawContacts._ID}, + Clauses.GROUP_HAS_ACCOUNT_AND_DATA_SET_AND_SOURCE_ID, + new String[]{ + sourceId, + accountWithDataSet.getAccountName(), + accountWithDataSet.getAccountType(), + accountWithDataSet.getDataSet() + }, null, null, null); + } try { if (c.moveToFirst()) { entry.groupId = c.getLong(0); } else { ContentValues groupValues = new ContentValues(); - groupValues.put(Groups.ACCOUNT_NAME, account.name); - groupValues.put(Groups.ACCOUNT_TYPE, account.type); + groupValues.put(Groups.ACCOUNT_NAME, accountWithDataSet.getAccountName()); + groupValues.put(Groups.ACCOUNT_TYPE, accountWithDataSet.getAccountType()); + groupValues.put(Groups.DATA_SET, accountWithDataSet.getDataSet()); groupValues.put(Groups.SOURCE_ID, sourceId); long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues); if (groupId < 0) { diff --git a/src/com/android/providers/contacts/TransactionContext.java b/src/com/android/providers/contacts/TransactionContext.java index c7eb03c..a4e8b8b 100644 --- a/src/com/android/providers/contacts/TransactionContext.java +++ b/src/com/android/providers/contacts/TransactionContext.java @@ -19,8 +19,6 @@ package com.android.providers.contacts; import com.google.android.collect.Maps; import com.google.android.collect.Sets; -import android.accounts.Account; - import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -33,24 +31,25 @@ import java.util.Set; */ public class TransactionContext { - private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap(); + private HashMap<Long, AccountWithDataSet> mInsertedRawContacts = Maps.newHashMap(); private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet(); - private HashMap<Long, Account> mInsertedProfileRawContacts = Maps.newHashMap(); + private HashMap<Long, AccountWithDataSet> mInsertedProfileRawContacts = Maps.newHashMap(); private HashSet<Long> mDirtyRawContacts = Sets.newHashSet(); private HashSet<Long> mStaleSearchIndexRawContacts = Sets.newHashSet(); private HashSet<Long> mStaleSearchIndexContacts = Sets.newHashSet(); private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap(); - public void rawContactInserted(long rawContactId, Account account) { - mInsertedRawContacts.put(rawContactId, account); + public void rawContactInserted(long rawContactId, AccountWithDataSet accountWithDataSet) { + mInsertedRawContacts.put(rawContactId, accountWithDataSet); } public void rawContactUpdated(long rawContactId) { mUpdatedRawContacts.add(rawContactId); } - public void profileRawContactInserted(long rawContactId, Account account) { - mInsertedProfileRawContacts.put(rawContactId, account); + public void profileRawContactInserted(long rawContactId, + AccountWithDataSet accountWithDataSet) { + mInsertedProfileRawContacts.put(rawContactId, accountWithDataSet); } public void markRawContactDirty(long rawContactId) { @@ -77,7 +76,7 @@ public class TransactionContext { return mUpdatedRawContacts; } - public Map<Long, Account> getInsertedProfileRawContactIds() { + public Map<Long, AccountWithDataSet> getInsertedProfileRawContactIds() { return mInsertedProfileRawContacts; } @@ -97,7 +96,7 @@ public class TransactionContext { return mUpdatedSyncStates.entrySet(); } - public Account getAccountForRawContact(long rawContactId) { + public AccountWithDataSet getAccountWithDataSetForRawContact(long rawContactId) { return mInsertedRawContacts.get(rawContactId); } diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index 17158ae..bb1b686 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -33,7 +33,6 @@ import android.content.Entity; import android.content.EntityIterator; import android.content.res.AssetFileDescriptor; import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.provider.ContactsContract; import android.provider.ContactsContract.AggregationExceptions; @@ -173,6 +172,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { RawContacts.CONTACT_ID, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, + RawContacts.DATA_SET, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.SOURCE_ID, RawContacts.VERSION, RawContacts.RAW_CONTACT_IS_USER_PROFILE, @@ -237,6 +238,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.STATUS_ICON, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, + RawContacts.DATA_SET, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.SOURCE_ID, RawContacts.VERSION, RawContacts.DIRTY, @@ -384,6 +387,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.STATUS_ICON, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, + RawContacts.DATA_SET, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.SOURCE_ID, RawContacts.VERSION, RawContacts.DELETED, @@ -433,6 +438,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { RawContacts.CONTACT_ID, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, + RawContacts.DATA_SET, + RawContacts.ACCOUNT_TYPE_AND_DATA_SET, RawContacts.SOURCE_ID, RawContacts.VERSION, RawContacts.DIRTY, @@ -500,6 +507,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Groups._ID, Groups.ACCOUNT_NAME, Groups.ACCOUNT_TYPE, + Groups.DATA_SET, + Groups.ACCOUNT_TYPE_AND_DATA_SET, Groups.SOURCE_ID, Groups.DIRTY, Groups.VERSION, @@ -528,6 +537,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Groups._ID, Groups.ACCOUNT_NAME, Groups.ACCOUNT_TYPE, + Groups.DATA_SET, + Groups.ACCOUNT_TYPE_AND_DATA_SET, Groups.SOURCE_ID, Groups.DIRTY, Groups.VERSION, diff --git a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java index 2539203..ba34c60 100644 --- a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java +++ b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java @@ -149,7 +149,7 @@ public class SynchronousContactsProvider2 extends ContactsProvider2 { } @Override - protected boolean isWritableAccount(String accountType) { + protected boolean isWritableAccountWithDataSet(String accountType) { return !READ_ONLY_ACCOUNT_TYPE.equals(accountType); } |