ContactsProvider2.java revision 67dde51ab932dc84d95a203b113989b13437f13d
14f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/*
24f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Copyright (C) 2009 The Android Open Source Project
34f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
44f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License");
54f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * you may not use this file except in compliance with the License.
64f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * You may obtain a copy of the License at
74f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
84f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *      http://www.apache.org/licenses/LICENSE-2.0
94f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Unless required by applicable law or agreed to in writing, software
114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS,
124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * See the License for the specific language governing permissions and
144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * limitations under the License
154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
1828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar
1967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport com.android.internal.content.SyncStateContentProviderHelper;
2028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.AggregatesColumns;
2128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.AggregationExceptionColumns;
2228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Clauses;
2328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.ContactsColumns;
2428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.DataColumns;
2528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.GroupsColumns;
2628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.MimetypesColumns;
27e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PhoneColumns;
2867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport com.android.providers.contacts.OpenHelper.PackagesColumns;
2928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
3028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Tables;
31e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikovimport com.google.android.collect.Lists;
32e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
33b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.content.ContentProvider;
3567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentProviderOperation;
3667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentProviderResult;
3735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
3867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
3967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
4035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
4167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.EntityIterator;
427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.OperationApplicationException;
4367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
4467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.pm.PackageManager;
454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
46ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
47b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
50c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
52619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.os.Binder;
53b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
54508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
55de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract;
566bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikovimport android.provider.Contacts.ContactMethods;
57de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Aggregates;
58b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
59de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds;
60de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Contacts;
61de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Data;
62ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.Groups;
631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkeyimport android.provider.ContactsContract.Presence;
646bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikovimport android.provider.ContactsContract.Aggregates.AggregationSuggestions;
653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
66ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
67ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
71de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
724097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
7367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
74a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
75a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
7600d71133c63c882fb41729ddc3a52f66fb155374Evan Millarimport android.util.Log;
774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
79b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
85035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintanapublic class ContactsProvider2 extends ContentProvider {
86b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    // TODO: clean up debug tag and rename this class
87b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private static final String TAG = "ContactsProvider ~~~~";
884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
89619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
90619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
91619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
95a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
97d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_ORDER_BY = Aggregates.STARRED + " DESC, "
98d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.TIMES_CONTACTED + " DESC, "
99d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.DISPLAY_NAME + " ASC";
100d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
101d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            "(SELECT COUNT(1) FROM " + Tables.AGGREGATES + " WHERE "
102d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.STARRED + "=1) + 25";
103d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1046bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES = 1000;
1056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES_ID = 1001;
1066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES_DATA = 1002;
1071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int AGGREGATES_SUMMARY = 1003;
1081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int AGGREGATES_SUMMARY_ID = 1004;
109ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int AGGREGATES_SUMMARY_FILTER = 1005;
110d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final int AGGREGATES_SUMMARY_STREQUENT = 1006;
111d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final int AGGREGATES_SUMMARY_STREQUENT_FILTER = 1007;
112b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private static final int AGGREGATES_SUMMARY_GROUP = 1008;
1134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS = 2002;
1156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_ID = 2003;
1166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_DATA = 2004;
11728ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey    private static final int CONTACTS_FILTER_EMAIL = 2005;
1184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
121ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
122ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES_FILTER = 3003;
123ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int POSTALS = 3004;
124a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
127b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
128b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
129b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE = 7000;
1311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE_ID = 7001;
1321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
13331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
13431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
135ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
137ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
138ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
13935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
14035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
14167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface ContactsQuery {
14267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.CONTACTS;
1439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
145ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
14667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Contacts.ACCOUNT_NAME,
14767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Contacts.ACCOUNT_TYPE,
148ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
15067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int CONTACT_ID = 0;
15167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_NAME = 1;
15267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_TYPE = 2;
15367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
15467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
15567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface DataContactsQuery {
15667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES;
15767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
15867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
1593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            ContactsColumns.CONCRETE_ID,
1603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
1613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Contacts.AGGREGATE_ID,
1623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Contacts.IS_RESTRICTED,
1633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.MIMETYPE,
164ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
16667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int CONTACT_ID = 0;
16767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
16867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int AGGREGATE_ID = 2;
16967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int IS_RESTRICTED = 3;
17067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int MIMETYPE = 4;
171ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
17267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
17367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
17467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface DataAggregatesQuery {
17567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES;
17667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
17767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
178ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
1793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
1803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.CONCRETE_ID,
1813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.CONCRETE_ID,
1823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Phone.NUMBER,
1833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Email.DATA,
1843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID,
1853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
1863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID,
1873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
188ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
189ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
19067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int CONTACT_ID = 0;
19167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
19267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int AGGREGATE_ID = 2;
19367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int MIMETYPE_ID = 3;
19467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int PHONE_NUMBER = 4;
19567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int EMAIL_DATA = 5;
19667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int OPTIMAL_PHONE_ID = 6;
19767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int FALLBACK_PHONE_ID = 7;
19867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int OPTIMAL_EMAIL_ID = 8;
19967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int FALLBACK_EMAIL_ID = 9;
20080c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
201ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DisplayNameQuery {
20467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
2093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA2,
2103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            StructuredName.DISPLAY_NAME,
2113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 0;
2143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 1;
2153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA2 = 2;
2163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DISPLAY_NAME = 3;
2173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DataQuery {
22067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.CONTACT_ID,
2263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
2273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA1,
2283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA2,
2293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA3,
2303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA4,
2313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA5,
2323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA6,
2333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA7,
2343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA8,
2353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA9,
2363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA10,
23767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA11,
23867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA12,
23967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA13,
24067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA14,
24167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA15,
2423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int ID = 0;
2453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int CONTACT_ID = 2;
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
2483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA1 = 4;
2493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA2 = 5;
2503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA3 = 6;
2513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA4 = 7;
2523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA5 = 8;
2533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA6 = 9;
2543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA7 = 10;
2553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA8 = 11;
2563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA9 = 12;
2573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA10 = 13;
25867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA11 = 14;
25967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA12 = 15;
26067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA13 = 16;
26167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA14 = 17;
26267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA15 = 18;
2633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    // Higher number represents higher priority in choosing what data to use for the display name
2663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_EMAIL = 1;
2673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_PHONE = 2;
2683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_ORGANIZATION = 3;
2693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_STRUCTURED_NAME = 4;
2703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final HashMap<String, Integer> sDisplayNamePriorities;
2723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    static {
2733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities = new HashMap<String, Integer>();
2743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(StructuredName.CONTENT_ITEM_TYPE,
2753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_STRUCTURED_NAME);
2763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Organization.CONTENT_ITEM_TYPE,
2773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_ORGANIZATION);
2783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Phone.CONTENT_ITEM_TYPE,
2793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_PHONE);
2803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Email.CONTENT_ITEM_TYPE,
2813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_EMAIL);
2823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
28331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /** Contains just the contacts columns */
2856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final HashMap<String, String> sAggregatesProjectionMap;
28600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar    /** Contains the aggregate columns along with primary phone */
2871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final HashMap<String, String> sAggregatesSummaryProjectionMap;
288de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    /** Contains the data, contacts, and aggregate columns, for joined tables. */
289de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    private static final HashMap<String, String> sDataContactsAggregateProjectionMap;
2909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data, contacts, group sourceid and aggregate columns, for joined tables. */
2919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataContactsGroupsAggregateProjectionMap;
292a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the contacts columns */
2934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
294a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the data columns */
2959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataGroupsProjectionMap;
2969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
2979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataContactsGroupsProjectionMap;
298a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains the data and contacts columns, for joined tables */
299a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final HashMap<String, String> sDataContactsProjectionMap;
300ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
301ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
302ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
303ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
304b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    /** Contains the just the agg_exceptions columns */
305b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
3067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
307c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the contact id associated with a data record. */
308c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdSelect;
309c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the mimetype id associated with a data record. */
310c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedMimetypeSelect;
311c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the aggregate id associated with a contact record. */
312c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedAggregateIdSelect;
313c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns a list of contact ids associated with an aggregate record. */
314c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdListSelect;
315c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
316c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "primary" is selected.*/
317c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetPrimaryWhere;
318c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
319c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "super primary" is selected.*/
320c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetSuperPrimaryWhere;
321b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /** Sql where statement for filtering on groups. */
322b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private static final String sAggregatesInGroupSelect;
323c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
324c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
326c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
3273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for incrementing times contacted for an aggregate */
328f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private SQLiteStatement mLastTimeContactedUpdate;
3293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
3303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private SQLiteStatement mContactDisplayNameUpdate;
331a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
3321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final String GTALK_PROTOCOL_STRING = ContactMethods
3331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            .encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
3341f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
3364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
337a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
3386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates", AGGREGATES);
3396bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#", AGGREGATES_ID);
3406bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/data", AGGREGATES_DATA);
3411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary", AGGREGATES_SUMMARY);
3421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/#", AGGREGATES_SUMMARY_ID);
343ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/filter/*",
344ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                AGGREGATES_SUMMARY_FILTER);
345d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/",
346d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT);
347d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/filter/*",
348d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT_FILTER);
349b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/group/*",
350b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                AGGREGATES_SUMMARY_GROUP);
35131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/suggestions",
35231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
3544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
355a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
356b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter_email/*",
357b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                CONTACTS_FILTER_EMAIL);
358b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
3604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
361ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
362ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
363ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
3641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
365ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
366ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
367ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
368ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
36935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
37035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
371a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
372b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
373b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
374b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
375b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
3764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
377bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
378bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
3791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
380fec4e13316f2731d84394e5fa2f93af3febdc20cEvan Millar        HashMap<String, String> columns;
3814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        // Aggregates projection map
3836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns = new HashMap<String, String>();
38400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(Aggregates._ID, "aggregates._id AS _id");
38567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.DISPLAY_NAME, AggregatesColumns.CONCRETE_DISPLAY_NAME + " AS "
38667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.DISPLAY_NAME);
38767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.LAST_TIME_CONTACTED, AggregatesColumns.CONCRETE_LAST_TIME_CONTACTED
38867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + " AS " + Aggregates.LAST_TIME_CONTACTED);
38967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.TIMES_CONTACTED, AggregatesColumns.CONCRETE_TIMES_CONTACTED + " AS "
39067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.TIMES_CONTACTED);
39167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.STARRED, AggregatesColumns.CONCRETE_STARRED + " AS "
39267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.STARRED);
393ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Aggregates.IN_VISIBLE_GROUP, Aggregates.IN_VISIBLE_GROUP);
394ae6ca1f34cf5458d79ec803411d4308879a91e92Evan Millar        columns.put(Aggregates.PHOTO_ID, Aggregates.PHOTO_ID);
395de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.put(Aggregates.PRIMARY_PHONE_ID, Aggregates.PRIMARY_PHONE_ID);
396c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Aggregates.PRIMARY_EMAIL_ID, Aggregates.PRIMARY_EMAIL_ID);
39767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.CUSTOM_RINGTONE, AggregatesColumns.CONCRETE_CUSTOM_RINGTONE + " AS "
39867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.CUSTOM_RINGTONE);
39967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.SEND_TO_VOICEMAIL, AggregatesColumns.CONCRETE_SEND_TO_VOICEMAIL
40067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + " AS " + Aggregates.SEND_TO_VOICEMAIL);
401619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
402619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID);
403619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
404619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID);
4056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        sAggregatesProjectionMap = columns;
4066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
4071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Aggregates primaries projection map. The overall presence status is
4081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // the most-present value, as indicated by the largest value.
4091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns = new HashMap<String, String>();
4101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns.putAll(sAggregatesProjectionMap);
41100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE);
41200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.LABEL, CommonDataKinds.Phone.LABEL);
41300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.NUMBER, CommonDataKinds.Phone.NUMBER);
414bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        columns.put(Presence.PRESENCE_STATUS, "MAX(" + Presence.PRESENCE_STATUS + ")");
4151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        sAggregatesSummaryProjectionMap = columns;
41600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts projection map
4184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
4194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Contacts._ID, "contacts._id AS _id");
4206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Contacts.AGGREGATE_ID, Contacts.AGGREGATE_ID);
4219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.ACCOUNT_NAME,
4229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_ACCOUNT_NAME + " as " + Contacts.ACCOUNT_NAME);
4239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.ACCOUNT_TYPE,
4249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_ACCOUNT_TYPE + " as " + Contacts.ACCOUNT_TYPE);
4259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.SOURCE_ID,
4269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_SOURCE_ID + " as " + Contacts.SOURCE_ID);
4279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.VERSION,
4289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_VERSION + " as " + Contacts.VERSION);
4299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.DIRTY,
4309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_DIRTY + " as " + Contacts.DIRTY);
4314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        sContactsProjectionMap = columns;
4324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Data projection map
4344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
4354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data._ID, "data._id AS _id");
4364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data.CONTACT_ID, Data.CONTACT_ID);
43767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.RES_PACKAGE, PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE);
438508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        columns.put(Data.MIMETYPE, Data.MIMETYPE);
439c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
440c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
441f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
4427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA1, "data.data1 as data1");
4437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA2, "data.data2 as data2");
4447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA3, "data.data3 as data3");
4457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA4, "data.data4 as data4");
4467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA5, "data.data5 as data5");
4477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA6, "data.data6 as data6");
4487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA7, "data.data7 as data7");
4497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA8, "data.data8 as data8");
4507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA9, "data.data9 as data9");
4517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA10, "data.data10 as data10");
45267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA11, "data.data11 as data11");
45367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA12, "data.data12 as data12");
45467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA13, "data.data13 as data13");
45567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA14, "data.data14 as data14");
45667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA15, "data.data15 as data15");
45767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupsColumns.CONCRETE_SOURCE_ID + " AS "
45867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + GroupMembership.GROUP_SOURCE_ID);
459d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        // Mappings used for backwards compatibility.
460d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put("number", Phone.NUMBER);
4619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataGroupsProjectionMap = columns;
462a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data, groups and contacts projection map for joins. _id comes from the data table
464a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns = new HashMap<String, String>();
465a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns.putAll(sContactsProjectionMap);
4669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
467ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
4689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataContactsGroupsProjectionMap = columns;
4699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
4709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
4719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
4729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataContactsGroupsProjectionMap);
4739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
474a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        sDataContactsProjectionMap = columns;
4757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
476de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        // Data and contacts projection map for joins. _id comes from the data table
477de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns = new HashMap<String, String>();
478de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.putAll(sAggregatesProjectionMap);
4797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.putAll(sContactsProjectionMap); //
4809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
4829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataContactsGroupsAggregateProjectionMap = columns;
4839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
4849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
4859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
4869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataContactsGroupsAggregateProjectionMap);
4879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
488de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        sDataContactsAggregateProjectionMap = columns;
489c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
490ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
491ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
492ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups._ID, "groups._id AS _id");
493035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
494035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
4959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
4969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
4979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
49867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.RES_PACKAGE, PackagesColumns.PACKAGE + " AS " + Groups.RES_PACKAGE);
499ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
50067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
501ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
502ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
503ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
504ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Contacts and groups projection map
505ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
506ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
507ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
508ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + AggregatesColumns.CONCRETE_ID
509ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
510ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
511ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
512ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
513ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
514ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.CONCRETE_ID + ") FROM "
515ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
516ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
517ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + " AND " + Clauses.HAS_PRIMARY_PHONE + ") AS " + Groups.SUMMARY_WITH_PHONES);
518ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
519ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
520ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
521b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
522b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
523b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
524b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
525127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.AGGREGATE_ID,
526127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                "contacts1." + Contacts.AGGREGATE_ID + " AS " + AggregationExceptions.AGGREGATE_ID);
527127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.CONTACT_ID, AggregationExceptionColumns.CONTACT_ID2);
528b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
529b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
530c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdSelect = "SELECT " + Data.CONTACT_ID + " FROM " + Tables.DATA + " WHERE "
531c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + Data._ID + "=?";
532c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedMimetypeSelect = "SELECT " + DataColumns.MIMETYPE_ID + " FROM " + Tables.DATA
533c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Data._ID + "=?";
534c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedAggregateIdSelect = "SELECT " + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS
535c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts._ID + "=(" + sNestedContactIdSelect + ")";
536c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdListSelect = "SELECT " + Contacts._ID + " FROM " + Tables.CONTACTS
537c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts.AGGREGATE_ID + "=(" + sNestedAggregateIdSelect + ")";
538c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sSetPrimaryWhere = Data.CONTACT_ID + "=(" + sNestedContactIdSelect + ") AND "
539c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
540b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sSetSuperPrimaryWhere = Data.CONTACT_ID + " IN (" + sNestedContactIdListSelect + ") AND "
541c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
542b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sAggregatesInGroupSelect = AggregatesColumns.CONCRETE_ID + " IN (SELECT "
543b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS + " WHERE ("
544b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + ContactsColumns.CONCRETE_ID + " IN (SELECT " + Tables.DATA + "."
545b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.CONTACT_ID + " FROM " + Tables.DATA_JOIN_MIMETYPES + " WHERE ("
546b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + "' AND "
547b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + GroupMembership.GROUP_ROW_ID + "=(SELECT " + Tables.GROUPS + "."
548b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Groups._ID + " FROM " + Tables.GROUPS + " WHERE " + Groups.TITLE + "=?)))))";
5494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
5504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
5523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
5533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
5543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
5553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
5573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
5593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
5603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
5613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
5633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
5643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
5653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
566e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
567e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
568e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
569e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
570e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                setIsPrimary(dataId);
571e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
572e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
573e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            fixContactDisplayName(db, contactId);
574e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
5753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
5763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
5783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
5793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
5803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
5813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
5823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            throw new UnsupportedOperationException();
5833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
5843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
5863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long dataId = c.getLong(DataQuery.ID);
5873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long contactId = c.getLong(DataQuery.CONTACT_ID);
5883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            boolean primary = c.getInt(DataQuery.IS_PRIMARY) != 0;
5893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
5903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
5913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                fixPrimary(db, contactId);
592e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                fixContactDisplayName(db, contactId);
5933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
5943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
5953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
5963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long contactId) {
5983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, contactId);
5993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
6003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                ContactsProvider2.this.setIsPrimary(newPrimaryId);
6013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
6023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long contactId) {
605e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
606e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
6073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Cursor c = queryData(db, contactId);
6083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
609e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
610e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    long dataId = c.getLong(DataQuery.ID);
611e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    int type = c.getInt(DataQuery.DATA2);
612e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
613e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
614e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
615e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
6163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
6173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
6183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
6193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
620e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
621e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
622e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
623e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
624e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
625e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
626e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
627e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
628e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
6293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long contactId) {
6323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO Lookup integer mimetype IDs' instead of joining for speed
63367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            return db.query(DataQuery.TABLE, DataQuery.COLUMNS, Data.CONTACT_ID + "="
6343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    + contactId + " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
6353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
6363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected void fixContactDisplayName(SQLiteDatabase db, long contactId) {
639e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (!sDisplayNamePriorities.containsKey(mMimetype)) {
640e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                return;
641e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
642e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
6433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String bestDisplayName = null;
64467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Cursor c = db.query(DisplayNameQuery.TABLE, DisplayNameQuery.COLUMNS,
6453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    Data.CONTACT_ID + "=" + contactId, null, null, null, null);
6463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
6473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                int maxPriority = -1;
6483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                while (c.moveToNext()) {
6493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String mimeType = c.getString(DisplayNameQuery.MIMETYPE);
6503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    boolean primary;
6513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String name;
6523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
6543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        name = c.getString(DisplayNameQuery.DISPLAY_NAME);
6553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = true;
6563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    } else {
6573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        name = c.getString(DisplayNameQuery.DATA2);
6583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
6593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
6603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    if (primary && name != null) {
6623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        Integer priority = sDisplayNamePriorities.get(mimeType);
6633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        if (priority != null && priority > maxPriority) {
6643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            maxPriority = priority;
6653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            bestDisplayName = name;
6663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        }
6673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
6683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
6693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
6713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
6723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
6733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            ContactsProvider2.this.setDisplayName(contactId, bestDisplayName);
6753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
6773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
6793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
6813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
6823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
6843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
6863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final NameSplitter mNameSplitter;
6883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public StructuredNameRowHandler(NameSplitter nameSplitter) {
6903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
6913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mNameSplitter = nameSplitter;
6923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
6953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
6963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            fixStructuredNameComponents(values);
697e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return super.insert(db, contactId, values);
6983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
7023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO Parse the full name if it has changed and replace pre-existing piece parts.
7033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
7063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Parses the supplied display name, but only if the incoming values do not already contain
7073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * structured name parts.  Also, if the display name is not provided, generate one by
7083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * concatenating first name and last name
7093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         *
7103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * TODO see if the order of first and last names needs to be conditionally reversed for
7113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * some locales, e.g. China.
7123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
7133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private void fixStructuredNameComponents(ContentValues values) {
7143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String fullName = values.getAsString(StructuredName.DISPLAY_NAME);
7153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (!TextUtils.isEmpty(fullName)
7163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.PREFIX))
7173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.GIVEN_NAME))
7183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.MIDDLE_NAME))
7193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.FAMILY_NAME))
7203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.SUFFIX))) {
7213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
7223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                mNameSplitter.split(name, fullName);
7233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.PREFIX, name.getPrefix());
7253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.GIVEN_NAME, name.getGivenNames());
7263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.MIDDLE_NAME, name.getMiddleName());
7273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.FAMILY_NAME, name.getFamilyName());
7283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.SUFFIX, name.getSuffix());
7293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (TextUtils.isEmpty(fullName)) {
7323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                String givenName = values.getAsString(StructuredName.GIVEN_NAME);
7333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                String familyName = values.getAsString(StructuredName.FAMILY_NAME);
7343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                if (TextUtils.isEmpty(givenName)) {
7353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = familyName;
7363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                } else if (TextUtils.isEmpty(familyName)) {
7373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = givenName;
7383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                } else {
7393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = givenName + " " + familyName;
7403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
7413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                if (!TextUtils.isEmpty(fullName)) {
7433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    values.put(StructuredName.DISPLAY_NAME, fullName);
7443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
7453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
7483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
7503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
7523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
7533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
7553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
7563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
7573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
7583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
7623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int type;
7633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String label;
7643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (values.containsKey(mTypeColumn)) {
7653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                type = values.getAsInteger(mTypeColumn);
7663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } else {
7673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                type = BaseTypes.TYPE_CUSTOM;
7683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (values.containsKey(mLabelColumn)) {
7703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                label = values.getAsString(mLabelColumn);
7713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } else {
7723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                label = null;
7733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (type != BaseTypes.TYPE_CUSTOM && label != null) {
7763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                throw new RuntimeException(mLabelColumn + " value can only be specified with "
7773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        + mTypeColumn + "=" + BaseTypes.TYPE_CUSTOM + "(custom)");
7783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (type == BaseTypes.TYPE_CUSTOM && label == null) {
7813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                throw new RuntimeException(mLabelColumn + " value must be specified when "
7823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        + mTypeColumn + "=" + BaseTypes.TYPE_CUSTOM + "(custom)");
7833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
785e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return super.insert(db, contactId, values);
7863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
7903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO read the data and check the constraint
7913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
7933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
7953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
7973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
7983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
8023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long id = super.insert(db, contactId, values);
8033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            fixContactDisplayName(db, contactId);
8043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return id;
8053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
8093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
8103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
8113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
8123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
8133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
8143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
8173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
818e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
819e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
820e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
821e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
822e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
823e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
824e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
825e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
826e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long id = super.insert(db, contactId, values);
827e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            fixContactDisplayName(db, contactId);
828e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return id;
829e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
830e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
831e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
832e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
833e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
834e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
835e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
836e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
837e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
838e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
839e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
840e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
841e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
842e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
8433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
8443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
8463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
8473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
851e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            ContentValues phoneValues = new ContentValues();
852e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String number = values.getAsString(Phone.NUMBER);
853e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
854e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
855e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
856e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
857e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
858e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
8593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long id = super.insert(db, contactId, values);
8603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
861e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
862e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.CONTACT_ID, contactId);
863e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, id);
864e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
865e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
866e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
8673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return id;
8693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
8733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
8743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
8753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
8763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
8773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
8783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
8793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
8803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
8813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
8823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
8833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
8863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
88853056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
8894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
89031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
891a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    private ContactAggregator mContactAggregator;
8924097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
893f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
894a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
895a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
89653056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
897a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
898a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
899a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
900a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
901a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
90253056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
90353056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
904a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
9054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
9064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
908b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        final Context context = getContext();
90935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
91031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        mOpenHelper = getOpenHelper(context);
9111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
9124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
91353056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mContactAggregator = new ContactAggregator(context, mOpenHelper, mAggregationScheduler);
914a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
915c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
916c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_PRIMARY
917c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetPrimaryWhere);
918c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
919c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_SUPER_PRIMARY
920c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetSuperPrimaryWhere);
921f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
922f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                + Contacts.TIMES_CONTACTED + "=" + Contacts.TIMES_CONTACTED + "+1,"
923f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                + Contacts.LAST_TIME_CONTACTED + "=? WHERE " + Contacts.AGGREGATE_ID + "=?");
924a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
9253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mContactDisplayNameUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
9263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                + ContactsColumns.DISPLAY_NAME + "=? WHERE " + Contacts._ID + "=?");
9273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
92828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
92928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
93028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
93128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
93228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_conjunctions));
9334097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
934f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mOpenHelper, this);
9353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
9373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
938e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
9393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
9403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
94167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
94267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
9433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
9443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
94567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
94667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL));
9473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
9483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
9493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
9514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
95331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
95431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
95531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
95631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
95731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
958a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
959a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
960a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
961a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
962a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
963a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
964a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
965a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
966a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
967a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
968a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
969a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
970a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
971a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
972a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
973a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
974a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
975a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Called when a change has been made.
976a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
977a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param uri the uri that the change was made to
978a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
979a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private void onChange(Uri uri) {
980a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null);
981a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
982a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
9834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean isTemporary() {
9854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return false;
9864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
9883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
9893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
9903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
9913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
9923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
9933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
9953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
9963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Uri insert(Uri uri, ContentValues values) {
999a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1000a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
100135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1002a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
100335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
100435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                id = mOpenHelper.getSyncState().insert(mOpenHelper.getWritableDatabase(), values);
100535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
100635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
10076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
1008f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                insertAggregate(values);
10096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
10106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
10116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1012a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS: {
1013f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
1014f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                id = insertContact(values, account);
1015a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1016a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1017a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1018a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
1019a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                values.put(Data.CONTACT_ID, uri.getPathSegments().get(1));
1020035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
1021a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1022a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1023a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1024a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1025035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
1026a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1027a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1028a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1029ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1030ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
1031ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                id = insertGroup(values, account);
1032ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1033ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1034ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
10351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
10361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
10371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
10381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
10391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1040a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
1041f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1042a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1043a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
10447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
10457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
10467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
10477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final Uri result = ContentUris.withAppendedId(uri, id);
1049a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        onChange(result);
1050a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return result;
1051a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1052a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1053a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1054035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
1055035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
1056035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
1057035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
1058035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
10597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1060035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
1061035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
1062035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountName = values.getAsString(Contacts.ACCOUNT_NAME);
1063035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountType = values.getAsString(Contacts.ACCOUNT_TYPE);
1064035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
1065035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
1066035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
1067035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
1068035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
1069035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
1070035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1071035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
1072035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_NAME, account.mName);
1073035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_TYPE, account.mType);
1074035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1075035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
10767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
10777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
10796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * Inserts an item in the aggregates table
10806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
10816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
10826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
10836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
10846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private long insertAggregate(ContentValues values) {
1085a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregates are created automatically");
10866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
10876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
10886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
1089a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
1090a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1091a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1092f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
1093a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1094a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1095f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private long insertContact(ContentValues values, Account account) {
10966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        /*
10976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * The contact record is inserted in the contacts table, but it needs to
10986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * be processed by the aggregator before it will be returned by the
10996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * "aggregates" queries.
11006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         */
1101a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
11026bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1103a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
1104a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        overriddenValues.putNull(Contacts.AGGREGATE_ID);
1105f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
11067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
11077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
11087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1109f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        long contactId = db.insert(Tables.CONTACTS, Contacts.AGGREGATE_ID, overriddenValues);
1110f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1111f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = Contacts.AGGREGATION_MODE_DEFAULT;
1112f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        if (values.containsKey(Contacts.AGGREGATION_MODE)) {
1113f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            aggregationMode = values.getAsInteger(Contacts.AGGREGATION_MODE);
1114f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
1115a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1116f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        triggerAggregation(contactId, aggregationMode);
1117a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1118f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        return contactId;
1119a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1120a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1121a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1122a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
1123a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1124a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1125a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1126a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1127035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private long insertData(ContentValues values) {
1128f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = Contacts.AGGREGATION_MODE_DISABLED;
1129a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1130a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1131a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
1132a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        db.beginTransaction();
1133a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        try {
1134a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            long contactId = values.getAsLong(Data.CONTACT_ID);
1135a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
113667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // Replace package with internal mapping
113767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            final String packageName = values.getAsString(Data.RES_PACKAGE);
113867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            if (packageName != null) {
113967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                values.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
114067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            }
114167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            values.remove(Data.RES_PACKAGE);
114267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
1143619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // Replace mimetype with internal mapping
1144508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            final String mimeType = values.getAsString(Data.MIMETYPE);
1145b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            values.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
1146508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            values.remove(Data.MIMETYPE);
1147508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
11483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
11499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
11509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
11519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (containsGroupSourceId && containsGroupId) {
11529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalArgumentException(
11539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
11549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                                    + "and GroupMembership.GROUP_ROW_ID");
11559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
11569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
11579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!containsGroupSourceId && !containsGroupId) {
11589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalArgumentException(
11599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
11609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                                    + "and GroupMembership.GROUP_ROW_ID");
11619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
11629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
11639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (containsGroupSourceId) {
11649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
11659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    final long groupId = getOrMakeGroup(db, contactId, sourceId);
11669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    values.remove(GroupMembership.GROUP_SOURCE_ID);
11679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    values.put(GroupMembership.GROUP_ROW_ID, groupId);
11689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
11694097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            }
11704097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
11713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            id = getDataRowHandler(mimeType).insert(db, contactId, values);
1172a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1173f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            aggregationMode = mContactAggregator.markContactForAggregation(contactId);
1174a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1175a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.setTransactionSuccessful();
1176a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        } finally {
1177a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.endTransaction();
1178a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1179a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1180f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        triggerAggregation(id, aggregationMode);
1181a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
11824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1184f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private void triggerAggregation(long contactId, int aggregationMode) {
1185f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
1186f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_DEFAULT:
1187f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.schedule();
1188f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1189f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1190f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_IMMEDITATE:
1191f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.aggregateContact(contactId);
1192f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1193f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1194f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_DISABLED:
1195f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                // Do nothing
1196f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1197f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
1198f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
1199f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1200e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
12013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
120267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        Cursor c = db.query(DataQuery.TABLE, DataQuery.COLUMNS,
12033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
1204e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        // TODO apply restrictions
12053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        try {
12063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (!c.moveToFirst()) {
12073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return 0;
12083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1210e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String mimeType = c.getString(DataQuery.MIMETYPE);
1211e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            boolean valid = false;
1212e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
1213e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
1214e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    valid = true;
1215e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    break;
12163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
1217e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1218e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1219e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (!valid) {
1220e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                throw new RuntimeException("Data type mismatch: expected "
1221e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
12223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return getDataRowHandler(mimeType).delete(db, c);
12253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } finally {
12263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            c.close();
12273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1230a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
12319261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * Returns the group id of the group with sourceId and the same account as contactId.
12329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
12339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
12349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param contactId the contact this group is associated with
12359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
12369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
12379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
12389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
12399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
12409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private long getOrMakeGroup(SQLiteDatabase db, long contactId, String sourceId) {
12419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
124267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, Contacts._ID + "="
124367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + contactId, null, null, null, null);
12449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
12459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
124667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountName = c.getString(ContactsQuery.ACCOUNT_NAME);
124767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountType = c.getString(ContactsQuery.ACCOUNT_TYPE);
12489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
12499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
12509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
12519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
12529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
12539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
12549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
12559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
12569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
12579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
12589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
12599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
12609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
12619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
12629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // as the contact refered to by contactId
12639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        c = db.query(Tables.GROUPS, new String[]{Contacts._ID},
12649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
12659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                new String[]{sourceId, account.mName, account.mType}, null, null, null);
12669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
12679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
12689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
12699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
12709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
12719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.mName);
12729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.mType);
12739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
12749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
12759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
12769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
12779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
12789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
12799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
12809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
12819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
12829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
12839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
12849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
12859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
12869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
1287ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Delete the given {@link Data} row, fixing up any {@link Aggregates}
1288ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * primaries that reference it.
1289ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1290ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private int deleteData(long dataId) {
1291ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1292ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1293ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimePhone = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
1294ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimeEmail = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1295ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1296ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Check to see if the data about to be deleted was a super-primary on
1297ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // the parent aggregate, and set flags to fix-up once deleted.
1298ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long aggId = -1;
1299ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long mimeId = -1;
1300ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        String dataRaw = null;
1301ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixOptimal = false;
1302ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixFallback = false;
1303ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1304ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor cursor = null;
1305ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        try {
130667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            cursor = db.query(DataAggregatesQuery.TABLE, DataAggregatesQuery.PROJECTION,
130767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
1308ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor.moveToFirst()) {
130967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                aggId = cursor.getLong(DataAggregatesQuery.AGGREGATE_ID);
131067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                mimeId = cursor.getLong(DataAggregatesQuery.MIMETYPE_ID);
1311ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
131267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    dataRaw = cursor.getString(DataAggregatesQuery.PHONE_NUMBER);
131367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixOptimal = (cursor.getLong(DataAggregatesQuery.OPTIMAL_PHONE_ID) == dataId);
131467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixFallback = (cursor.getLong(DataAggregatesQuery.FALLBACK_PHONE_ID) == dataId);
1315ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
131667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    dataRaw = cursor.getString(DataAggregatesQuery.EMAIL_DATA);
131767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixOptimal = (cursor.getLong(DataAggregatesQuery.OPTIMAL_EMAIL_ID) == dataId);
131867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixFallback = (cursor.getLong(DataAggregatesQuery.FALLBACK_EMAIL_ID) == dataId);
1319ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1320ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1321ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        } finally {
1322ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor != null) {
1323ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.close();
1324ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor = null;
1325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1328ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Delete the requested data item.
1329ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        int dataDeleted = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
1330ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1331ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Fix-up any super-primary values that are now invalid.
1332ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (fixOptimal || fixFallback) {
1333ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final ContentValues values = new ContentValues();
1334ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final StringBuilder scoreClause = new StringBuilder();
1335ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1336ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String SCORE = "score";
1337ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1338ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Build scoring clause that will first pick data items under the
1339ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // same aggregate that have identical values, otherwise fall back to
1340ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // normal primary scoring from the member contacts.
1341ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("(CASE WHEN ");
1342ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (mimeId == mimePhone) {
1343ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Phone.NUMBER);
1344ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            } else if (mimeId == mimeEmail) {
1345ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Email.DATA);
1346ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1347ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("=");
1348ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            DatabaseUtils.appendEscapedSQLString(scoreClause, dataRaw);
1349ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append(" THEN 2 ELSE " + Data.IS_PRIMARY + " END) AS " + SCORE);
1350ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1351ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String[] PROJ_PRIMARY = new String[] {
1352ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    DataColumns.CONCRETE_ID,
1353ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Contacts.IS_RESTRICTED,
1354ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    scoreClause.toString(),
1355ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            };
1356ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1357ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_DATA_ID = 0;
1358ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_IS_RESTRICTED = 1;
135967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            final int COL_SCORE = 2;
1360ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1361ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES, PROJ_PRIMARY,
1362ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND " + DataColumns.MIMETYPE_ID
1363ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            + "=" + mimeId, null, null, null, SCORE);
1364ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1365ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixOptimal) {
1366ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
136767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                String colIsRestricted = null;
1368ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
1369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID;
137067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    colIsRestricted = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_IS_RESTRICTED;
1371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
1372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID;
137367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    colIsRestricted = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_IS_RESTRICTED;
1374ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1375ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1376ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixOptimal told us that
1377ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
1378ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
137967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                values.putNull(colIsRestricted);
1380ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1381ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // When finding a new optimal primary, we only care about the
1382ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // highest scoring value, regardless of source.
1383ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (cursor.moveToFirst()) {
1384ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimal = cursor.getLong(COL_DATA_ID);
138567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    final long newIsRestricted = cursor.getLong(COL_IS_RESTRICTED);
1386ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1387ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimal != 0) {
1388ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, newOptimal);
1389ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
139067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    if (newIsRestricted != 0) {
139167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                        values.put(colIsRestricted, newIsRestricted);
1392ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
1393ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1394ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1395ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1396ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixFallback) {
1397ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
1398ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
1399ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID;
1400ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
1401ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID;
1402ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1403ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1404ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixFallback told us that
1405ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
1406ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
1407ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1408ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // The best fallback value is the highest scoring data item that
1409ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // hasn't been restricted.
1410ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.moveToPosition(-1);
1411ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                while (cursor.moveToNext()) {
1412ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final boolean isRestricted = (cursor.getInt(COL_IS_RESTRICTED) == 1);
1413ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (!isRestricted) {
1414ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, cursor.getLong(COL_DATA_ID));
1415ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        break;
1416ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
1417ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1418ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1419ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1420ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Push through any aggregate updates we have
1421ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (values.size() > 0) {
1422ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                db.update(Tables.AGGREGATES, values, AggregatesColumns.CONCRETE_ID + "=" + aggId,
1423ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        null);
1424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1425ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1426ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1427ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return dataDeleted;
1428ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1429ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1430ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
1431ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
1432ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1433ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private long insertGroup(ContentValues values, Account account) {
1434ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1435ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1436ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
1437ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
1438ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
1439ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1440ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1441ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
144267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE);
144367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
144467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            overriddenValues.put(GroupsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
144567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
144667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        overriddenValues.remove(Groups.RES_PACKAGE);
1447ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1448ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return db.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
1449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1451ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
14521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
14531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
14541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private long insertPresence(ContentValues values) {
14551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
14561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
14571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String protocol = values.getAsString(Presence.IM_PROTOCOL);
14581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(handle) || TextUtils.isEmpty(protocol)) {
14591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            throw new IllegalArgumentException("IM_PROTOCOL and IM_HANDLE are required");
14601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
14611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
14621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
14631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        boolean matchEmail = GTALK_PROTOCOL_STRING.equals(protocol);
14641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
14651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String selection;
14661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
14671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
14681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = "(" + Clauses.WHERE_IM_MATCHES + ") OR (" + Clauses.WHERE_EMAIL_MATCHES + ")";
14691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle, handle };
14701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
14711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = Clauses.WHERE_IM_MATCHES;
14721f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle };
14731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
14741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
14751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
14761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long aggId = -1;
14771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
14781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
147967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            cursor = db.query(DataContactsQuery.TABLE,
148067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    DataContactsQuery.PROJECTION, selection, selectionArgs, null, null, null);
14811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
148267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
148367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                aggId = cursor.getLong(DataContactsQuery.AGGREGATE_ID);
14841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
14851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
14861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
14871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
14881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
148931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
149031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
149131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
14921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
14931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
14941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.DATA_ID, dataId);
14951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.AGGREGATE_ID, aggId);
14961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
14971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Insert the presence update
14981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long presenceId = db.replace(Tables.PRESENCE, null, values);
14991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return presenceId;
15001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
15011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
15024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
15034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int delete(Uri uri, String selection, String[] selectionArgs) {
1504508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1505508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
1506508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
150735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
150835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().delete(db, selection, selectionArgs);
150935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
15106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: {
15116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                long aggregateId = ContentUris.parseId(uri);
15126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
15136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                // Remove references to the aggregate first
15146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                ContentValues values = new ContentValues();
15156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                values.putNull(Contacts.AGGREGATE_ID);
1516b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.update(Tables.CONTACTS, values, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
15176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1518b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return db.delete(Tables.AGGREGATES, BaseColumns._ID + "=" + aggregateId, null);
15196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
15206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1521508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case CONTACTS_ID: {
1522508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long contactId = ContentUris.parseId(uri);
1523b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int contactsDeleted = db.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
1524b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, Data.CONTACT_ID + "=" + contactId, null);
1525508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                return contactsDeleted + dataDeleted;
1526508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1527508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
1528508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID: {
1529508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
1530ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return deleteData(dataId);
1531ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1532ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1533ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1534ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1535ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final long groupMembershipMimetypeId = mOpenHelper
1536ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1537ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int groupsDeleted = db.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
1538ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
1539ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
1540ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupId, null);
1541ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1542ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return groupsDeleted + dataDeleted;
1543508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1544508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
15451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
15461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return db.delete(Tables.PRESENCE, null, null);
15471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
15481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1549508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            default:
15503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
1551508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
15524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
15534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1554f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private static Account readAccountFromQueryParams(Uri uri) {
1555035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String name = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1556035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String type = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1557f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
1558f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            return null;
1559f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        }
1560f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        return new Account(name, type);
1561f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    }
1562f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
1563ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
15644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
15654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
156600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
156735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
156800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
156900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
157000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
157135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
157235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().update(db, values, selection, selectionArgs);
157335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1574c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            // TODO(emillar): We will want to disallow editing the aggregates table at some point.
157500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES: {
157600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                count = db.update(Tables.AGGREGATES, values, selection, selectionArgs);
157700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
157800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
157900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
158000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES_ID: {
1581d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                count = updateAggregateData(db, ContentUris.parseId(uri), values);
1582c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
1583c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
1584c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1585c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            case DATA_ID: {
1586c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsSuperPrimary = values.containsKey(Data.IS_SUPER_PRIMARY);
1587c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsPrimary = values.containsKey(Data.IS_PRIMARY);
1588c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                final long id = ContentUris.parseId(uri);
1589c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1590c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // Remove primary or super primary values being set to 0. This is disallowed by the
1591c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // content provider.
15928b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsSuperPrimary && values.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
1593c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsSuperPrimary = false;
1594c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
1595c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
15968b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsPrimary && values.getAsInteger(Data.IS_PRIMARY) == 0) {
1597c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsPrimary = false;
1598c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1599c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1600c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1601c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (containsIsSuperPrimary) {
1602c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsSuperPrimary(id);
1603c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1604c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1605c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting these, remove them from "values".
1606c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
1607c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    if (containsIsPrimary) {
1608c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                        values.remove(Data.IS_PRIMARY);
1609c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    }
1610c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                } else if (containsIsPrimary) {
1611c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1612c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1613c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting this, remove it from "values".
1614c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1615c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1616c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1617c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (values.size() > 0) {
1618c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    String selectionWithId = (Data._ID + " = " + ContentUris.parseId(uri) + " ")
1619f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                            + (selection == null ? "" : " AND " + selection);
1620c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    count = db.update(Tables.DATA, values, selectionWithId, selectionArgs);
1621c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
162200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
162300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
16247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS: {
16267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selection, selectionArgs);
16277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
16287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
16297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID: {
16317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selectionWithId = (Contacts._ID + " = " + ContentUris.parseId(uri) + " ")
16327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        + (selection == null ? "" : " AND " + selection);
16337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selectionWithId, selectionArgs);
16347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Log.i(TAG, "Selection is: " + selectionWithId);
16357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
16367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
16377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case DATA: {
16397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.DATA, values, selection, selectionArgs);
16407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
16417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
16427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1643ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1644ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selection, selectionArgs);
1645ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1646ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1647ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1648ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1649ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1650ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1651ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String selectionWithId = (Groups._ID + "=" + groupId + " ")
1652ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + (selection == null ? "" : " AND " + selection);
1653ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selectionWithId, selectionArgs);
1654ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1655ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // If changing visibility, then update aggregates
1656ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (values.containsKey(Groups.GROUP_VISIBLE)) {
1657ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    mOpenHelper.updateAllVisible();
1658ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1659ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1660ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1661ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1662ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1663127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1664127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                count = updateAggregationException(db, values);
1665b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1666b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1667b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
16687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
1669f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
167000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
167100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
167200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        if (count > 0) {
167300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            getContext().getContentResolver().notifyChange(uri, null);
167400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
167500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
16764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
16774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1678d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private int updateAggregateData(SQLiteDatabase db, long aggregateId, ContentValues values) {
1679d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1680d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // First update all constituent contacts
1681f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        ContentValues optionValues = new ContentValues(5);
1682f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyStringValue(optionValues, Contacts.CUSTOM_RINGTONE,
1683f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.CUSTOM_RINGTONE);
1684f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.SEND_TO_VOICEMAIL,
1685f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.SEND_TO_VOICEMAIL);
1686f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.LAST_TIME_CONTACTED,
1687f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.LAST_TIME_CONTACTED);
1688f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.TIMES_CONTACTED,
1689f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.TIMES_CONTACTED);
1690f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.STARRED,
1691f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.STARRED);
1692d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1693d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
1694d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (optionValues.size() == 0) {
1695d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
1696d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1697d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1698f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        db.update(Tables.CONTACTS, optionValues, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
1699f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        return db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggregateId, null);
1700f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
1701d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1702f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    public void updateContactTime(long aggregateId, long lastTimeContacted) {
1703f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
1704f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(2, aggregateId);
1705f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.execute();
1706d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1707d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1708127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private static class ContactPair {
1709127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId1;
1710127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId2;
1711127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1712127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        /**
1713127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         * Constructor that ensures that this.contactId1 &lt; this.contactId2
1714127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         */
1715127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public ContactPair(long contactId1, long contactId2) {
1716127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (contactId1 < contactId2) {
1717127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId1;
1718127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId2;
1719127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1720127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId1;
1721127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId2;
1722127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1723127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1724127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    }
172580c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1726127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
1727127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
1728127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long aggregateId = values.getAsInteger(AggregationExceptions.AGGREGATE_ID);
1729127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long contactId = values.getAsInteger(AggregationExceptions.CONTACT_ID);
173080c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1731127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // First, we build a list of contactID-contactID pairs for the given aggregate and contact.
1732127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ArrayList<ContactPair> pairs = new ArrayList<ContactPair>();
173367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, Contacts.AGGREGATE_ID
173467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + "=" + aggregateId, null, null, null, null);
1735127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        try {
1736127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            while (c.moveToNext()) {
173767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                long aggregatedContactId = c.getLong(ContactsQuery.CONTACT_ID);
1738e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                if (aggregatedContactId != contactId) {
1739e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                    pairs.add(new ContactPair(aggregatedContactId, contactId));
1740e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                }
1741b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1742b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        } finally {
1743b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            c.close();
1744b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
1745127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1746127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // Now we iterate through all contact pairs to see if we need to insert/delete/update
1747127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // the corresponding exception
1748127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ContentValues exceptionValues = new ContentValues(3);
1749127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
1750127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        for (ContactPair pair : pairs) {
1751127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            final String whereClause =
1752127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    AggregationExceptionColumns.CONTACT_ID1 + "=" + pair.contactId1 + " AND "
1753127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    + AggregationExceptionColumns.CONTACT_ID2 + "=" + pair.contactId2;
1754127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
1755127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.delete(Tables.AGGREGATION_EXCEPTIONS, whereClause, null);
1756127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1757127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID1, pair.contactId1);
1758127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID2, pair.contactId2);
1759127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
1760127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        exceptionValues);
1761127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1762127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1763127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1764f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = mContactAggregator.markContactForAggregation(contactId);
1765f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        if (aggregationMode != Contacts.AGGREGATION_MODE_DISABLED) {
1766f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            mContactAggregator.aggregateContact(db, contactId);
1767f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC
1768f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                    || exceptionType == AggregationExceptions.TYPE_KEEP_OUT) {
1769f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.updateAggregateData(aggregateId);
1770f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            }
17717a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        }
1772127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1773127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
1774127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
1775127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
1776b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
1777b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1778619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1779619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list.
1780619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1781619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private boolean isContained(String[] array, String value) {
1782bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array != null) {
1783bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            for (String test : array) {
1784bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                if (value.equals(test)) {
1785bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                    return true;
1786bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                }
1787619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1788619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1789619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return false;
1790619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1791619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1792619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1793619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list, and add to the
1794619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * array if the value doesn't already appear.
1795619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1796619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private String[] assertContained(String[] array, String value) {
1797bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array == null) {
1798bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            array = new String[] {value};
1799bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        } else if (!isContained(array, value)) {
1800619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            String[] newArray = new String[array.length + 1];
1801619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            System.arraycopy(array, 0, newArray, 0, array.length);
1802619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            newArray[array.length] = value;
1803619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            array = newArray;
1804619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1805619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return array;
1806619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1807619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
18084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
18104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
18114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
181235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1813d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
18141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
18151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String limit = null;
1816bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        String aggregateIdColName = Tables.AGGREGATES + "." + Aggregates._ID;
18174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1818619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
1819619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
1820a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
18214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
182235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
182335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
182435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
182535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
18266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
1827b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1828619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1829619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
1830619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sAggregatesProjectionMap);
1831619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1832619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1833619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1834619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case AGGREGATES_ID: {
1835619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = ContentUris.parseId(uri);
1836619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1837ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1838619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1839619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
18406bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                qb.setProjectionMap(sAggregatesProjectionMap);
18416bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
18426bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
18436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
18441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY: {
1845619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
18461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1847619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1848619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1849619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
18501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1851bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
18521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
18531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
18541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
18551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY_ID: {
1856619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
18571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                long aggId = ContentUris.parseId(uri);
18581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1859ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1860619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1861619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1862619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
18631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1864bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
18651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
18661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
18671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1868ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case AGGREGATES_SUMMARY_FILTER: {
1869619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1870ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1871ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1872ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1873ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1874ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1875bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
1876ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1877ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1878ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1879d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT_FILTER:
1880d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT: {
1881d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the first query for starred
1882d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1883d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1884d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1885d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1886d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1887d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1888d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String starredQuery = qb.buildQuery(projection, Aggregates.STARRED + "=1",
1889bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null,
1890d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        null /* limit */);
1891d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1892d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
1893d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
1894d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1895d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1896d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1897d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1898d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1899d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1900d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
1901d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Aggregates.TIMES_CONTACTED + " > 0 AND (" + Aggregates.STARRED
1902d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        + " = 0 OR " + Aggregates.STARRED + " IS NULL)",
1903bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null, null);
1904d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1905d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
1906d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
1907d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
1908d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                Cursor c = db.rawQueryWithFactory(null, query, null,
1909d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1910d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1911d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if ((c != null) && !isTemporary()) {
1912d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
1913d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
1914d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1915d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
1916d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
1917d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1918b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            case AGGREGATES_SUMMARY_GROUP: {
1919b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1920b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregateRestrictionExceptions(qb);
1921b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1922b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
1923b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1924b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
1925b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    qb.appendWhere(" AND " + sAggregatesInGroupSelect);
1926b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    selectionArgs = appendGroupArg(selectionArgs, uri.getLastPathSegment());
1927b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
1928b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                groupBy = aggregateIdColName;
1929b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
1930b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
1931b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
19326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_DATA: {
1933619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = Long.parseLong(uri.getPathSegments().get(1));
193467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES_GROUPS);
19359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataContactsGroupsAggregateProjectionMap);
1936619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Contacts.AGGREGATE_ID + "=" + aggId + " AND ");
1937619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
19386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
19396bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
194000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
1941ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
194267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
1943ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1944ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
1945ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1946ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(" AND " + buildAggregateLookupWhereClause(
1947ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                            uri.getLastPathSegment()));
1948ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1949ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1950ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1951ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1952ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES: {
195367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
1954ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1955ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Phone.CONTENT_ITEM_TYPE + "\"");
1956ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1957ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1958ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1959ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
196067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
1961ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
196267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.appendWhere(Data.MIMETYPE + " = \"" + StructuredPostal.CONTENT_ITEM_TYPE + "\"");
1963ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1964ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1965ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
19664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: {
196767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.CONTACTS);
19684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1969619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
19704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
19714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
19724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
19734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: {
1974619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = ContentUris.parseId(uri);
197567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.CONTACTS);
19764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1977ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(ContactsColumns.CONCRETE_ID + "=" + contactId + " AND ");
1978619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
19794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
19804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
19814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1982a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
1983619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = Long.parseLong(uri.getPathSegments().get(1));
198467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
19859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataContactsGroupsProjectionMap);
1986619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Data.CONTACT_ID + "=" + contactId + " AND ");
1987619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
1988a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1989a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1990a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
199128ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey            case CONTACTS_FILTER_EMAIL: {
1992619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
199367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
1994e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.setProjectionMap(sDataContactsProjectionMap);
19955d0f923eb4c5351ebf323cc6f19c82acff98693eJeff Sharkey                qb.appendWhere(Data.MIMETYPE + "='" + CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'");
1996e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhere(" AND " + CommonDataKinds.Email.DATA + "=");
1997e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhereEscapeString(uri.getPathSegments().get(2));
1998e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
1999e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
2000e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
2001e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
2002035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
2003035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
2004343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                if (!TextUtils.isEmpty(accountName)) {
2005035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                    qb.appendWhere(Contacts.ACCOUNT_NAME + "="
2006035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountName) + " AND "
2007035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + Contacts.ACCOUNT_TYPE + "="
2008035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountType) + " AND ");
2009343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                }
201067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
20119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
2012619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
2013e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
2014e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
2015e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
20164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
201767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
20189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
2019ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri) + " AND ");
2020619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
20214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
20224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
20234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2024a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
2025619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
2026a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
2027a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
2028a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
2029e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                    sortOrder = Data.CONTACT_ID;
2030a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
2031a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                final String number = uri.getLastPathSegment();
2033bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                OpenHelper.buildPhoneLookupQuery(qb, number);
2034a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                qb.setProjectionMap(sDataContactsProjectionMap);
2035a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2036a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2037a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2038ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2039ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
2040ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
2041ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2042ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2043ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2044ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2045ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
2046ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
2047ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
2048ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(GroupsColumns.CONCRETE_ID + "=" + groupId);
2049ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2050ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2051ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2052ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
2053ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES_DATA_CONTACTS_AGGREGATES);
2054ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
2055ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                groupBy = GroupsColumns.CONCRETE_ID;
2056ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2057ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2058ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2059b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2060127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS_JOIN_CONTACTS);
2061b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
2062b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2063b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2064b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
206531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
206631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                long aggregateId = Long.parseLong(uri.getPathSegments().get(1));
206731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final String maxSuggestionsParam =
206831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        uri.getQueryParameter(AggregationSuggestions.MAX_SUGGESTIONS);
206931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
207031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
207131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                if (maxSuggestionsParam != null) {
207231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(maxSuggestionsParam);
207331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
207431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
207531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
207631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
207731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(aggregateId, projection,
207831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        sAggregatesProjectionMap, maxSuggestions);
207931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
208031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
20814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
2082f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
2083f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                        sortOrder);
20844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
20854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
20864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Perform the query and set the notification uri
20871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final Cursor c = qb.query(db, projection, selection, selectionArgs,
2088bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy, null, sortOrder, limit);
20894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
20904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
20914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
20924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
20934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
20944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
20957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
209667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * List of package names with access to {@link Contacts#IS_RESTRICTED} data.
209767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     */
209867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private static final String[] sAllowedPackages = new String[] {
209967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        "com.android.contacts",
210067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        "com.facebook",
210167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    };
210267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
210367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    /**
210467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * Check if {@link Binder#getCallingUid()} should be allowed access to
210567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * {@link Contacts#IS_RESTRICTED} data.
210667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     */
210767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private boolean hasRestrictedAccess() {
210867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final PackageManager pm = getContext().getPackageManager();
210967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String[] callerPackages = pm.getPackagesForUid(Binder.getCallingUid());
211067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
211167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        // Has restricted access if caller matches any packages
211267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        for (String callerPackage : callerPackages) {
211367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            for (String allowedPackage : sAllowedPackages) {
211467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                if (allowedPackage.equals(callerPackage)) {
211567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    return true;
211667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                }
211767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            }
211867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
211967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        return false;
212067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
212167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
212267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    /**
2123619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Restrict selection of {@link Aggregates} to only public ones, or those
212467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * the caller has been granted an exception to.
2125619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2126619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregateRestrictionExceptions(SQLiteQueryBuilder qb) {
212767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
212867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.appendWhere("1");
212967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
213067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.appendWhere(AggregatesColumns.SINGLE_IS_RESTRICTED + "=0");
2131619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2132619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2133619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2134619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2135619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the calling process, and
2136619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * add projections to correctly select {@link Aggregates#PRIMARY_PHONE_ID}
2137619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * and {@link Aggregates#PRIMARY_EMAIL_ID}.
2138619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2139619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregatePrimaryRestrictionExceptions(HashMap<String, String> projection) {
214067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        String projectionPhone;
214167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        String projectionEmail;
214267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
214367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
214467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // With restricted access, always give optimal values
214567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionPhone = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID + " AS "
214667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_PHONE_ID;
214767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionEmail = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID + " AS "
214867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_EMAIL_ID;
214967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
215067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // With general access, always give fallback values
215167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionPhone = AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID + " AS "
215267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_PHONE_ID;
215367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionEmail = AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID + " AS "
215467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_EMAIL_ID;
215567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
215667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2157619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_PHONE_ID);
2158619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_PHONE_ID, projectionPhone);
2159619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2160619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_EMAIL_ID);
2161619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_EMAIL_ID, projectionEmail);
2162619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2163619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2164619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2165619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
2166619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
2167619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
2168619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2169619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyContactsRestrictionExceptions(SQLiteQueryBuilder qb) {
217067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
217167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.appendWhere("1");
217267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
217367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.appendWhere(Contacts.IS_RESTRICTED + "=0");
2174619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2175619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2176619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2177619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2178619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
2179619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
2180619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
2181619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2182e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    void applyDataRestrictionExceptions(SQLiteQueryBuilder qb) {
2183619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        applyContactsRestrictionExceptions(qb);
2184619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2185619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2186619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
21877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
21887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
21897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
21907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    private static class ContactsEntityIterator implements EntityIterator {
21917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
21927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
21937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
21957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data1",
21967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data2",
21977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data3",
21987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data4",
21997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data5",
22007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data6",
22017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data7",
22027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data8",
22037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data9",
220467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data10",
220567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data11",
220667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data12",
220767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data13",
220867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data14",
220967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data15"};
22107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
2212035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_NAME,
2213035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_TYPE,
2214035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.SOURCE_ID,
2215035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.VERSION,
2216035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.DIRTY,
2217035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data._ID,
221867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.RES_PACKAGE,
2219035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.MIMETYPE,
2220035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA1,
2221035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA2,
2222035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA3,
2223035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA4,
2224035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA5,
2225035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA6,
2226035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA7,
2227035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA8,
2228035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA9,
2229035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA10,
223067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA11,
223167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA12,
223267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA13,
223367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA14,
223467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA15,
2235035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.CONTACT_ID,
2236035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.IS_PRIMARY,
22379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Contacts.Data.DATA_VERSION,
22389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_SOURCE_ID};
2239035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
2240035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
2241035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
2242035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
2243035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
2244035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
2245035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
224667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
224767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
224867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
224967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_CONTACT_ID = 23;
225067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_IS_PRIMARY = 24;
225167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA_VERSION = 25;
225267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_GROUP_SOURCE_ID = 26;
22537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public ContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
22557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
22567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
22577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
22597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    ? Contacts.Data.CONTACT_ID
22607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    : (Contacts.Data.CONTACT_ID + "," + sortOrder);
22617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
22637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
226467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
22659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            qb.setProjectionMap(sDataContactsGroupsProjectionMap);
22667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
22677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                qb.appendWhere(Data.CONTACT_ID + "=" + contactsIdString);
22687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
2269035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
2270035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
2271035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
2272035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.appendWhere(Contacts.ACCOUNT_NAME + "="
2273035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
2274035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + Contacts.ACCOUNT_TYPE + "="
2275035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
2276035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
22777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
22787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
22797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
22807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
22817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
22837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
22847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
22857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
22867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
22877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
22887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
22897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
22917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
22927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
22937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
22947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
22967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
22977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
22987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
22997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
23007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
23017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
23027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
23037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
23047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
23057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
23077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final long contactId = c.getLong(COLUMN_CONTACT_ID);
23097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
23117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
2312035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
2313035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
23147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts._ID, contactId);
23157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.DIRTY, c.getLong(COLUMN_DIRTY));
2316f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            contactValues.put(Contacts.VERSION, c.getLong(COLUMN_VERSION));
23177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
23187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
23197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
23217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
23227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (contactId != c.getLong(COLUMN_CONTACT_ID)) {
23237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
23247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
23257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
23267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
23277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                dataValues.put(Contacts.Data._ID, c.getString(COLUMN_DATA_ID));
232867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataValues.put(Contacts.Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
2329f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
2330f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.IS_PRIMARY, c.getString(COLUMN_IS_PRIMARY));
2331f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
23329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
23339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
23349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
23359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
23369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
23377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                for (int i = 0; i < 10; i++) {
23387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
23397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
23407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
23417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
23427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
23437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
23447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
23457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
23467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
23477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
23487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
23497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
23507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
23517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
23527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
23537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
23547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
23567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
23577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
23587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2359a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
23607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
23617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
23627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
23637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
23647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS:
23657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID:
23667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
23677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (match == CONTACTS_ID) {
23687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
23697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
23707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                return new ContactsEntityIterator(this, contactsIdString,
23727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
23737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
23747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
23757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
23767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
23777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
23794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
2380a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
23814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
23826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: return Aggregates.CONTENT_TYPE;
23836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: return Aggregates.CONTENT_ITEM_TYPE;
23844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: return Contacts.CONTENT_TYPE;
23854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: return Contacts.CONTENT_ITEM_TYPE;
2386508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
23876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2388508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2389b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return mOpenHelper.getDataMimeType(dataId);
239031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: return AggregationExceptions.CONTENT_TYPE;
239131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTION_ID: return AggregationExceptions.CONTENT_ITEM_TYPE;
239231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: return Aggregates.CONTENT_TYPE;
23934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
2394a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        throw new UnsupportedOperationException("Unknown uri: " + uri);
23954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
23967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2397b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    @Override
23987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
23997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            throws OperationApplicationException {
24007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
24027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        db.beginTransaction();
24037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        try {
24047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentProviderResult[] results = super.applyBatch(operations);
24057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.setTransactionSuccessful();
24067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return results;
24077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        } finally {
24087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.endTransaction();
24097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
2411c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
24123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private void setDisplayName(long contactId, String displayName) {
24133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
24143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mContactDisplayNameUpdate.bindString(1, displayName);
24153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
24163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mContactDisplayNameUpdate.bindNull(1);
24173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
24183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mContactDisplayNameUpdate.bindLong(2, contactId);
24193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mContactDisplayNameUpdate.execute();
24203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
24213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2422c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
2423c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
2424c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
2425c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
2426c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
2427c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
2428c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsPrimary(long dataId) {
2429c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
2430c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(2, dataId);
2431c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(3, dataId);
2432c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
2433c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
2434c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
2435c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
2436c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
2437c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
2438c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
2439c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
2440c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
2441c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsSuperPrimary(long dataId) {
2442c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
2443c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(2, dataId);
2444c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(3, dataId);
2445c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
2446619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2447619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the parent aggregate and package for this new primary
2448619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2449619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2450619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long aggId = -1;
2451619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isRestricted = false;
2452619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        String mimeType = null;
2453619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2454619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        Cursor cursor = null;
2455619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        try {
245667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            cursor = db.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
245767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
2458619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor.moveToFirst()) {
245967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                aggId = cursor.getLong(DataContactsQuery.AGGREGATE_ID);
246067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                isRestricted = (cursor.getInt(DataContactsQuery.IS_RESTRICTED) == 1);
246167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                mimeType = cursor.getString(DataContactsQuery.MIMETYPE);
2462619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2463619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } finally {
2464619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor != null) {
2465619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                cursor.close();
2466619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2467619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2468619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2469619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Bypass aggregate update if no parent found, or if we don't keep track
2470619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // of super-primary for this mimetype.
2471d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (aggId == -1) {
2472d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return;
2473d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2474619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2475619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isPhone = CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
2476619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isEmail = CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimeType);
2477619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2478619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Record this value as the new primary for the parent aggregate
2479619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final ContentValues values = new ContentValues();
2480619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (isPhone) {
2481619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID, dataId);
248267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_IS_RESTRICTED, isRestricted);
2483619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (isEmail) {
2484619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID, dataId);
248567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_IS_RESTRICTED, isRestricted);
2486619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2487619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2488619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // If this data is unrestricted, then also set as fallback
2489619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (!isRestricted && isPhone) {
2490619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID, dataId);
2491619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (!isRestricted && isEmail) {
2492619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID, dataId);
2493619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2494619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2495619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Push update into aggregates table, if needed
2496619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (values.size() > 0) {
2497619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggId, null);
2498619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2499619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2500c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
2501ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2502ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private String buildAggregateLookupWhereClause(String filterParam) {
2503ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        StringBuilder filter = new StringBuilder();
2504ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.AGGREGATES);
2505ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(".");
2506ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Aggregates._ID);
2507ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" IN (SELECT ");
2508ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts.AGGREGATE_ID);
2509ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" FROM ");
2510ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.CONTACTS);
2511ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" WHERE ");
2512ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts._ID);
2513d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(" IN (SELECT  contact_id FROM name_lookup WHERE normalized_name GLOB '");
2514ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // NOTE: Query parameters won't work here since the SQL compiler
2515ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // needs to parse the actual string to know that it can use the
2516ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // index to do a prefix scan.
2517d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(NameNormalizer.normalize(filterParam) + "*");
2518d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append("'))");
2519ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        return filter.toString();
2520ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
2521ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2522b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private String[] appendGroupArg(String[] selectionArgs, String arg) {
2523b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
2524b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
2525b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
2526b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
2527b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
2528b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            System.arraycopy(selectionArgs, 0, newSelectionArgs, 0, selectionArgs.length);
2529b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            newSelectionArgs[newLength - 1] = arg;
2530b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
2531b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
2532b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
25334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
2534