summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Santoro <dsantoro@google.com>2011-07-13 14:03:53 -0700
committerDave Santoro <dsantoro@google.com>2011-07-27 17:15:46 -0700
commit43368a3f9e05a979e454e278d6a0e8475f08923d (patch)
tree3ff616a1aff1af5ea4d18d437b9ac8f2032e985a
parent51d1da962292ecef21abdf7e41abfdb7f1d72fcd (diff)
downloadpackages_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
-rw-r--r--src/com/android/providers/contacts/AccountWithDataSet.java71
-rw-r--r--src/com/android/providers/contacts/ContactAggregator.java149
-rw-r--r--src/com/android/providers/contacts/ContactDirectoryManager.java1
-rw-r--r--src/com/android/providers/contacts/ContactLookupKey.java13
-rw-r--r--src/com/android/providers/contacts/ContactsDatabaseHelper.java79
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java285
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java54
-rw-r--r--src/com/android/providers/contacts/TransactionContext.java19
-rw-r--r--tests/src/com/android/providers/contacts/ContactsProvider2Test.java13
-rw-r--r--tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java2
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);
}