Add support for visibility of ungrouped contacts.
Built a tricky UPDATE query to determine the visibility of
any Contact by using any GroupMembership entries, otherwise
fall back to Settings.UNGROUPED_VISIBLE.
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 4ceca6f..409b305 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -548,18 +548,15 @@
// RawContacts and groups projection map
columns = new HashMap<String, String>();
columns.putAll(sGroupsProjectionMap);
-
columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
+ ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
+ Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
+ ") AS " + Groups.SUMMARY_COUNT);
-
columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
+ ContactsColumns.CONCRETE_ID + ") FROM "
+ Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
+ Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
+ " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
-
sGroupsSummaryProjectionMap = columns;
// Aggregate exception projection map
@@ -574,12 +571,20 @@
// Settings projection map
columns = new HashMap<String, String>();
- columns.put(Settings._ID, Settings._ID);
columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
columns.put(Settings.SHOULD_SYNC_MODE, Settings.SHOULD_SYNC_MODE);
columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
+ columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(DISTINCT " + RawContacts.CONTACT_ID
+ + ") FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
+ + Clauses.UNGROUPED + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT + ") AS "
+ + Settings.UNGROUPED_COUNT);
+ columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(DISTINCT "
+ + RawContacts.CONTACT_ID + ") FROM "
+ + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
+ + Clauses.UNGROUPED + " AND " + Contacts.HAS_PHONE_NUMBER + " GROUP BY "
+ + Clauses.GROUP_BY_ACCOUNT + ") AS " + Settings.UNGROUPED_WITH_PHONES);
sSettingsProjectionMap = columns;
columns = new HashMap<String, String>();
@@ -1255,7 +1260,7 @@
}
case SETTINGS: {
- id = mDb.insert(Tables.SETTINGS, null, values);
+ id = insertSettings(values);
break;
}
@@ -1537,6 +1542,14 @@
return mDb.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
}
+ private long insertSettings(ContentValues values) {
+ final long id = mDb.insert(Tables.SETTINGS, null, values);
+ if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
+ mOpenHelper.updateAllVisible();
+ }
+ return id;
+ }
+
/**
* Inserts a presence update.
*/
@@ -1717,7 +1730,7 @@
}
case SETTINGS: {
- return mDb.delete(Tables.SETTINGS, selection, selectionArgs);
+ return deleteSettings(selection, selectionArgs);
}
case PRESENCE: {
@@ -1759,6 +1772,14 @@
}
}
+ private int deleteSettings(String selection, String[] selectionArgs) {
+ final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
+ if (count > 0) {
+ mOpenHelper.updateAllVisible();
+ }
+ return count;
+ }
+
public int deleteRawContact(long rawContactId, boolean permanently) {
// TODO delete aggregation exceptions
mOpenHelper.removeContactIfSingleton(rawContactId);
@@ -1856,7 +1877,7 @@
}
case SETTINGS: {
- count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
+ count = updateSettings(values, selection, selectionArgs);
break;
}
@@ -1889,6 +1910,14 @@
return count;
}
+ private int updateSettings(ContentValues values, String selection, String[] selectionArgs) {
+ final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
+ if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
+ mOpenHelper.updateAllVisible();
+ }
+ return count;
+ }
+
private int updateRawContact(long rawContactId, ContentValues values, String selection,
String[] selectionArgs) {
@@ -2426,6 +2455,18 @@
case SETTINGS: {
qb.setTables(Tables.SETTINGS);
qb.setProjectionMap(sSettingsProjectionMap);
+
+ // When requesting specific columns, this query requires
+ // late-binding of the GroupMembership MIME-type.
+ final String groupMembershipMimetypeId = Long.toString(mOpenHelper
+ .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
+ if (isContained(projection, Settings.UNGROUPED_COUNT)) {
+ selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
+ }
+ if (isContained(projection, Settings.UNGROUPED_WITH_PHONES)) {
+ selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
+ }
+
break;
}
diff --git a/src/com/android/providers/contacts/OpenHelper.java b/src/com/android/providers/contacts/OpenHelper.java
index 006a978..d555aa3 100644
--- a/src/com/android/providers/contacts/OpenHelper.java
+++ b/src/com/android/providers/contacts/OpenHelper.java
@@ -41,9 +41,7 @@
import android.provider.ContactsContract.Presence;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
-import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.SocialContract.Activities;
import android.telephony.PhoneNumberUtils;
@@ -60,7 +58,8 @@
/* package */ class OpenHelper extends SQLiteOpenHelper {
private static final String TAG = "OpenHelper";
- private static final int DATABASE_VERSION = 72;
+ private static final int DATABASE_VERSION = 73;
+
private static final String DATABASE_NAME = "contacts2.db";
private static final String DATABASE_PRESENCE = "presence_db";
@@ -114,6 +113,25 @@
public static final String RAW_CONTACTS_JOIN_CONTACTS = "raw_contacts "
+ "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
+ // NOTE: This requires late binding of GroupMembership MIME-type
+ public static final String RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS = "raw_contacts "
+ + "LEFT OUTER JOIN settings ON ("
+ + "raw_contacts.account_name = settings.account_name AND "
+ + "raw_contacts.account_type = settings.account_type) "
+ + "LEFT OUTER JOIN data ON (data.mimetype_id=? AND "
+ + "data.raw_contact_id = raw_contacts._id) "
+ + "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID
+ + ")";
+
+ // NOTE: This requires late binding of GroupMembership MIME-type
+ public static final String SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS = "settings "
+ + "LEFT OUTER JOIN raw_contacts ON ("
+ + "raw_contacts.account_name = settings.account_name AND "
+ + "raw_contacts.account_type = settings.account_type) "
+ + "LEFT OUTER JOIN data ON (data.mimetype_id=? AND "
+ + "data.raw_contact_id = raw_contacts._id) "
+ + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
+
public static final String DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS = "data "
+ "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
+ "LEFT OUTER JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
@@ -195,22 +213,31 @@
}
public interface Clauses {
- public static final String MIMETYPE_IS_GROUP_MEMBERSHIP = MimetypesColumns.CONCRETE_MIMETYPE
- + "='" + GroupMembership.CONTENT_ITEM_TYPE + "'";
+ final String MIMETYPE_IS_GROUP_MEMBERSHIP = MimetypesColumns.CONCRETE_MIMETYPE + "='"
+ + GroupMembership.CONTENT_ITEM_TYPE + "'";
- public static final String BELONGS_TO_GROUP = DataColumns.CONCRETE_GROUP_ID + "="
+ final String BELONGS_TO_GROUP = DataColumns.CONCRETE_GROUP_ID + "="
+ GroupsColumns.CONCRETE_ID;
- // TODO: add in check against package_visible
- public static final String IN_VISIBLE_GROUP = "SELECT MIN(COUNT(" + DataColumns.CONCRETE_ID
- + "),1) FROM " + Tables.DATA_JOIN_RAW_CONTACTS_GROUPS + " WHERE "
- + DataColumns.MIMETYPE_ID + "=? AND " + RawContacts.CONTACT_ID + "="
- + ContactsColumns.CONCRETE_ID + " AND " + Groups.GROUP_VISIBLE + "=1";
+ final String UNGROUPED = DataColumns.CONCRETE_GROUP_ID + " IS NULL";
- public static final String GROUP_HAS_ACCOUNT_AND_SOURCE_ID =
- Groups.SOURCE_ID + "=? AND "
- + Groups.ACCOUNT_NAME + "=? AND "
- + Groups.ACCOUNT_TYPE + "=?";
+ final String GROUP_BY_ACCOUNT = SettingsColumns.CONCRETE_ACCOUNT_NAME + ","
+ + SettingsColumns.CONCRETE_ACCOUNT_TYPE;
+
+ final String RAW_CONTACT_IS_LOCAL = RawContactsColumns.CONCRETE_ACCOUNT_NAME
+ + " IS NULL AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL";
+
+ final String ZERO_GROUP_MEMBERSHIPS = "COUNT(" + GroupsColumns.CONCRETE_ID + ")=0";
+
+ final String CONTACT_IS_VISIBLE = "SELECT (CASE WHEN " + RAW_CONTACT_IS_LOCAL
+ + " THEN 1 WHEN " + ZERO_GROUP_MEMBERSHIPS + " THEN " + Settings.UNGROUPED_VISIBLE
+ + " ELSE MAX(" + Groups.GROUP_VISIBLE + ") END) FROM "
+ + Tables.RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS + " WHERE "
+ + RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + " GROUP BY "
+ + RawContacts.CONTACT_ID;
+
+ final String GROUP_HAS_ACCOUNT_AND_SOURCE_ID = Groups.SOURCE_ID + "=? AND "
+ + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?";
}
public interface ContactsColumns {
@@ -376,6 +403,13 @@
public static final String CLUSTER = "cluster";
}
+ public interface SettingsColumns {
+ public static final String CONCRETE_ACCOUNT_NAME = Tables.SETTINGS + "."
+ + Settings.ACCOUNT_NAME;
+ public static final String CONCRETE_ACCOUNT_TYPE = Tables.SETTINGS + "."
+ + Settings.ACCOUNT_TYPE;
+ }
+
public interface PresenceColumns {
String RAW_CONTACT_ID = "presence_raw_contact_id";
}
@@ -414,7 +448,7 @@
private HashMap<String, String[]> mNicknameClusterCache;
/** Compiled statements for updating {@link Contacts#IN_VISIBLE_GROUP}. */
- private SQLiteStatement mVisibleAllUpdate;
+ private SQLiteStatement mVisibleUpdate;
private SQLiteStatement mVisibleSpecificUpdate;
private Delegate mDelegate;
@@ -477,10 +511,11 @@
+ NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.NAME_TYPE + ","
+ NameLookupColumns.NORMALIZED_NAME + ") VALUES (?,?,?)");
+ // Compile statements for updating visibility
final String visibleUpdate = "UPDATE " + Tables.CONTACTS + " SET "
- + Contacts.IN_VISIBLE_GROUP + "= (" + Clauses.IN_VISIBLE_GROUP + ")";
+ + Contacts.IN_VISIBLE_GROUP + "=(" + Clauses.CONTACT_IS_VISIBLE + ")";
- mVisibleAllUpdate = db.compileStatement(visibleUpdate);
+ mVisibleUpdate = db.compileStatement(visibleUpdate);
mVisibleSpecificUpdate = db.compileStatement(visibleUpdate + " WHERE "
+ ContactsColumns.CONCRETE_ID + "=?");
@@ -527,7 +562,7 @@
Contacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," +
Contacts.LAST_TIME_CONTACTED + " INTEGER," +
Contacts.STARRED + " INTEGER NOT NULL DEFAULT 0," +
- Contacts.IN_VISIBLE_GROUP + " INTEGER NOT NULL DEFAULT 1," +
+ Contacts.IN_VISIBLE_GROUP + " INTEGER DEFAULT 1," +
Contacts.HAS_PHONE_NUMBER + " INTEGER NOT NULL DEFAULT 0," +
ContactsColumns.SINGLE_IS_RESTRICTED + " INTEGER NOT NULL DEFAULT 0" +
");");
@@ -774,15 +809,13 @@
AggregationExceptionColumns.RAW_CONTACT_ID1 +
");");
- // Settings uses SYNC_MODE_UNSUPPORTED as default unless specified.
db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.SETTINGS + " (" +
Settings.ACCOUNT_NAME + " STRING NOT NULL," +
Settings.ACCOUNT_TYPE + " STRING NOT NULL," +
Settings.UNGROUPED_VISIBLE + " INTEGER NOT NULL DEFAULT 0," +
- Settings.SHOULD_SYNC_MODE + " INTEGER NOT NULL DEFAULT 0, " +
Settings.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1, " +
"PRIMARY KEY (" + Settings.ACCOUNT_NAME + ", " +
- Settings.ACCOUNT_TYPE + ") ON CONFLICT REPLACE" +
+ Settings.ACCOUNT_TYPE + ") ON CONFLICT REPLACE" +
");");
// The table for recent calls is here so we can do table joins
@@ -1194,8 +1227,8 @@
*/
public void updateAllVisible() {
final long groupMembershipMimetypeId = getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
- mVisibleAllUpdate.bindLong(1, groupMembershipMimetypeId);
- mVisibleAllUpdate.execute();
+ mVisibleUpdate.bindLong(1, groupMembershipMimetypeId);
+ mVisibleUpdate.execute();
}
/**