ContactsProvider2.java revision 70b5ee6864cb3368d24a9e876fb93008997b12df
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 {
156c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        public static final String TABLE = Tables.DATA_JOIN_MIMETYPE_CONTACTS;
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;
17167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
17267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
17367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface DataAggregatesQuery {
17467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES;
17567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
17667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
177ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
1783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
1793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.CONCRETE_ID,
1803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.CONCRETE_ID,
1813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Phone.NUMBER,
1823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Email.DATA,
1833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID,
1843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
1853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID,
1863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
187ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
188ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
18967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int CONTACT_ID = 0;
19067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
19167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int AGGREGATE_ID = 2;
19267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int MIMETYPE_ID = 3;
19367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int PHONE_NUMBER = 4;
19467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int EMAIL_DATA = 5;
19567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int OPTIMAL_PHONE_ID = 6;
19667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int FALLBACK_PHONE_ID = 7;
19767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int OPTIMAL_EMAIL_ID = 8;
19867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int FALLBACK_EMAIL_ID = 9;
19980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
200ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DisplayNameQuery {
20367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
2083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA2,
2093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            StructuredName.DISPLAY_NAME,
2103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 0;
2133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 1;
2143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA2 = 2;
2153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DISPLAY_NAME = 3;
2163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DataQuery {
21967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.CONTACT_ID,
2253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
2263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA1,
2273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA2,
2283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA3,
2293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA4,
2303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA5,
2313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA6,
2323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA7,
2333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA8,
2343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA9,
2353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA10,
23667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA11,
23767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA12,
23867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA13,
23967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA14,
24067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA15,
2413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int ID = 0;
2443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int CONTACT_ID = 2;
2463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA1 = 4;
2483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA2 = 5;
2493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA3 = 6;
2503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA4 = 7;
2513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA5 = 8;
2523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA6 = 9;
2533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA7 = 10;
2543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA8 = 11;
2553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA9 = 12;
2563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA10 = 13;
25767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA11 = 14;
25867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA12 = 15;
25967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA13 = 16;
26067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA14 = 17;
26167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA15 = 18;
2623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private interface DataIdQuery {
26520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String[] COLUMNS = { Data._ID };
26620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
26720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
26820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
26920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    // Higher number represents higher priority in choosing what data to use for the display name
2713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_EMAIL = 1;
2723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_PHONE = 2;
2733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_ORGANIZATION = 3;
2743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_STRUCTURED_NAME = 4;
2753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final HashMap<String, Integer> sDisplayNamePriorities;
2773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    static {
2783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities = new HashMap<String, Integer>();
2793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(StructuredName.CONTENT_ITEM_TYPE,
2803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_STRUCTURED_NAME);
2813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Organization.CONTENT_ITEM_TYPE,
2823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_ORGANIZATION);
2833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Phone.CONTENT_ITEM_TYPE,
2843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_PHONE);
2853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Email.CONTENT_ITEM_TYPE,
2863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_EMAIL);
2873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
28831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /** Contains just the contacts columns */
2906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final HashMap<String, String> sAggregatesProjectionMap;
29100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar    /** Contains the aggregate columns along with primary phone */
2921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final HashMap<String, String> sAggregatesSummaryProjectionMap;
293de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    /** Contains the data, contacts, and aggregate columns, for joined tables. */
294de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    private static final HashMap<String, String> sDataContactsAggregateProjectionMap;
2959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data, contacts, group sourceid and aggregate columns, for joined tables. */
2969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataContactsGroupsAggregateProjectionMap;
297a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the contacts columns */
2984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
299a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the data columns */
3009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataGroupsProjectionMap;
3019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
3029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataContactsGroupsProjectionMap;
303a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains the data and contacts columns, for joined tables */
304a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final HashMap<String, String> sDataContactsProjectionMap;
305ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
306ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
307ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
308ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
309b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    /** Contains the just the agg_exceptions columns */
310b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
3117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
312c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the contact id associated with a data record. */
313c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdSelect;
314c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the mimetype id associated with a data record. */
315c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedMimetypeSelect;
316c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the aggregate id associated with a contact record. */
317c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedAggregateIdSelect;
318c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns a list of contact ids associated with an aggregate record. */
319c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdListSelect;
320c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
321c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "primary" is selected.*/
322c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetPrimaryWhere;
323c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
324c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "super primary" is selected.*/
325c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetSuperPrimaryWhere;
326b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /** Sql where statement for filtering on groups. */
327b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private static final String sAggregatesInGroupSelect;
328c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
329c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
331c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
3323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for incrementing times contacted for an aggregate */
333f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private SQLiteStatement mLastTimeContactedUpdate;
3343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
3353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private SQLiteStatement mContactDisplayNameUpdate;
336a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
3371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final String GTALK_PROTOCOL_STRING = ContactMethods
3381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            .encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
3391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
3414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
342a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
3436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates", AGGREGATES);
3446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#", AGGREGATES_ID);
3456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/data", AGGREGATES_DATA);
3461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary", AGGREGATES_SUMMARY);
3471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/#", AGGREGATES_SUMMARY_ID);
348ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/filter/*",
349ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                AGGREGATES_SUMMARY_FILTER);
350d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/",
351d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT);
352d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/filter/*",
353d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT_FILTER);
354b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/group/*",
355b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                AGGREGATES_SUMMARY_GROUP);
35631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/suggestions",
35731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
3594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
360a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
361b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter_email/*",
362b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                CONTACTS_FILTER_EMAIL);
363b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
3654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
366ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
367ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
368ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
3691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
370ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
373ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
37435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
37535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
376a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
377b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
378b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
379b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
380b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
3814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
382bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
383bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
3841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
385fec4e13316f2731d84394e5fa2f93af3febdc20cEvan Millar        HashMap<String, String> columns;
3864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        // Aggregates projection map
3886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns = new HashMap<String, String>();
38900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(Aggregates._ID, "aggregates._id AS _id");
39067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.DISPLAY_NAME, AggregatesColumns.CONCRETE_DISPLAY_NAME + " AS "
39167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.DISPLAY_NAME);
39267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.LAST_TIME_CONTACTED, AggregatesColumns.CONCRETE_LAST_TIME_CONTACTED
39367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + " AS " + Aggregates.LAST_TIME_CONTACTED);
39467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.TIMES_CONTACTED, AggregatesColumns.CONCRETE_TIMES_CONTACTED + " AS "
39567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.TIMES_CONTACTED);
39667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.STARRED, AggregatesColumns.CONCRETE_STARRED + " AS "
39767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.STARRED);
398ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Aggregates.IN_VISIBLE_GROUP, Aggregates.IN_VISIBLE_GROUP);
399ae6ca1f34cf5458d79ec803411d4308879a91e92Evan Millar        columns.put(Aggregates.PHOTO_ID, Aggregates.PHOTO_ID);
400de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.put(Aggregates.PRIMARY_PHONE_ID, Aggregates.PRIMARY_PHONE_ID);
401c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Aggregates.PRIMARY_EMAIL_ID, Aggregates.PRIMARY_EMAIL_ID);
40267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.CUSTOM_RINGTONE, AggregatesColumns.CONCRETE_CUSTOM_RINGTONE + " AS "
40367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + Aggregates.CUSTOM_RINGTONE);
40467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Aggregates.SEND_TO_VOICEMAIL, AggregatesColumns.CONCRETE_SEND_TO_VOICEMAIL
40567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + " AS " + Aggregates.SEND_TO_VOICEMAIL);
406619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
407619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID);
408619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
409619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID);
4106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        sAggregatesProjectionMap = columns;
4116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
4121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns = new HashMap<String, String>();
4131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns.putAll(sAggregatesProjectionMap);
414c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov
415c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        // Aggregates primaries projection map. The overall presence status is
416c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        // the most-present value, as indicated by the largest value.
417c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        columns.put(Aggregates.PRESENCE_STATUS, "MAX(" + Presence.PRESENCE_STATUS + ")");
418c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        columns.put(Aggregates.PRIMARY_PHONE_TYPE, CommonDataKinds.Phone.TYPE);
419c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        columns.put(Aggregates.PRIMARY_PHONE_LABEL, CommonDataKinds.Phone.LABEL);
420c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        columns.put(Aggregates.PRIMARY_PHONE_NUMBER, CommonDataKinds.Phone.NUMBER);
4211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        sAggregatesSummaryProjectionMap = columns;
42200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts projection map
4244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
4254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Contacts._ID, "contacts._id AS _id");
4266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Contacts.AGGREGATE_ID, Contacts.AGGREGATE_ID);
4279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.ACCOUNT_NAME,
4289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_ACCOUNT_NAME + " as " + Contacts.ACCOUNT_NAME);
4299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.ACCOUNT_TYPE,
4309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_ACCOUNT_TYPE + " as " + Contacts.ACCOUNT_TYPE);
4319261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.SOURCE_ID,
4329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_SOURCE_ID + " as " + Contacts.SOURCE_ID);
4339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.VERSION,
4349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_VERSION + " as " + Contacts.VERSION);
4359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.DIRTY,
4369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_DIRTY + " as " + Contacts.DIRTY);
4374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        sContactsProjectionMap = columns;
4384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Data projection map
4404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
4414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data._ID, "data._id AS _id");
4424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data.CONTACT_ID, Data.CONTACT_ID);
44367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.RES_PACKAGE, PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE);
444508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        columns.put(Data.MIMETYPE, Data.MIMETYPE);
445c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
446c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
447f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
4487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA1, "data.data1 as data1");
4497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA2, "data.data2 as data2");
4507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA3, "data.data3 as data3");
4517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA4, "data.data4 as data4");
4527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA5, "data.data5 as data5");
4537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA6, "data.data6 as data6");
4547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA7, "data.data7 as data7");
4557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA8, "data.data8 as data8");
4567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA9, "data.data9 as data9");
4577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA10, "data.data10 as data10");
45867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA11, "data.data11 as data11");
45967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA12, "data.data12 as data12");
46067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA13, "data.data13 as data13");
46167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA14, "data.data14 as data14");
46267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA15, "data.data15 as data15");
46367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupsColumns.CONCRETE_SOURCE_ID + " AS "
46467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + GroupMembership.GROUP_SOURCE_ID);
46520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
46620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // TODO: remove this projection
467d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        // Mappings used for backwards compatibility.
468d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put("number", Phone.NUMBER);
4699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataGroupsProjectionMap = columns;
470a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data, groups and contacts projection map for joins. _id comes from the data table
472a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns = new HashMap<String, String>();
473a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns.putAll(sContactsProjectionMap);
4749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
4769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataContactsGroupsProjectionMap = columns;
4779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
4789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
4799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
4809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataContactsGroupsProjectionMap);
4819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
482a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        sDataContactsProjectionMap = columns;
4837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
484de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        // Data and contacts projection map for joins. _id comes from the data table
485de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns = new HashMap<String, String>();
486de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.putAll(sAggregatesProjectionMap);
4877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.putAll(sContactsProjectionMap); //
4889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
489ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
4909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataContactsGroupsAggregateProjectionMap = columns;
4919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
4929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
4939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
4949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataContactsGroupsAggregateProjectionMap);
4959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
496de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        sDataContactsAggregateProjectionMap = columns;
497c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
498ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
499ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
500ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups._ID, "groups._id AS _id");
501035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
502035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
5039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
5049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
5059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
50667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.RES_PACKAGE, PackagesColumns.PACKAGE + " AS " + Groups.RES_PACKAGE);
507ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
50867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
509ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
510ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
511ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
512ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Contacts and groups projection map
513ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
514ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
515ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
516ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + AggregatesColumns.CONCRETE_ID
517ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
518ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
519ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
520ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
521ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
522ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.CONCRETE_ID + ") FROM "
523ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
524ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
525ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + " AND " + Clauses.HAS_PRIMARY_PHONE + ") AS " + Groups.SUMMARY_WITH_PHONES);
526ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
527ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
528ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
529b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
530b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
531b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
532b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
533127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.AGGREGATE_ID,
534127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                "contacts1." + Contacts.AGGREGATE_ID + " AS " + AggregationExceptions.AGGREGATE_ID);
535127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.CONTACT_ID, AggregationExceptionColumns.CONTACT_ID2);
536b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
537b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
538c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdSelect = "SELECT " + Data.CONTACT_ID + " FROM " + Tables.DATA + " WHERE "
539c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + Data._ID + "=?";
540c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedMimetypeSelect = "SELECT " + DataColumns.MIMETYPE_ID + " FROM " + Tables.DATA
541c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Data._ID + "=?";
542c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedAggregateIdSelect = "SELECT " + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS
543c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts._ID + "=(" + sNestedContactIdSelect + ")";
544c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdListSelect = "SELECT " + Contacts._ID + " FROM " + Tables.CONTACTS
545c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts.AGGREGATE_ID + "=(" + sNestedAggregateIdSelect + ")";
546c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sSetPrimaryWhere = Data.CONTACT_ID + "=(" + sNestedContactIdSelect + ") AND "
547c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
548b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sSetSuperPrimaryWhere = Data.CONTACT_ID + " IN (" + sNestedContactIdListSelect + ") AND "
549c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
550b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sAggregatesInGroupSelect = AggregatesColumns.CONCRETE_ID + " IN (SELECT "
551b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS + " WHERE ("
552b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + ContactsColumns.CONCRETE_ID + " IN (SELECT " + Tables.DATA + "."
553b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.CONTACT_ID + " FROM " + Tables.DATA_JOIN_MIMETYPES + " WHERE ("
554b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + "' AND "
555b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + GroupMembership.GROUP_ROW_ID + "=(SELECT " + Tables.GROUPS + "."
556b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Groups._ID + " FROM " + Tables.GROUPS + " WHERE " + Groups.TITLE + "=?)))))";
5574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
5584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
5603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
5613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
5623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
5633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
5653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
5673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
5683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
5693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
5713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
5723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
5733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
574e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
575e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
576e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
577e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
578e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                setIsPrimary(dataId);
579e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
580e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
581e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            fixContactDisplayName(db, contactId);
582e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
5833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
5843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
5863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
5873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
5883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
5893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
5903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            throw new UnsupportedOperationException();
5913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
5923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
5933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
5943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long dataId = c.getLong(DataQuery.ID);
5953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long contactId = c.getLong(DataQuery.CONTACT_ID);
5963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            boolean primary = c.getInt(DataQuery.IS_PRIMARY) != 0;
5973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
5983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
5993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                fixPrimary(db, contactId);
600e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                fixContactDisplayName(db, contactId);
6013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
6023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
6033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long contactId) {
6063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, contactId);
6073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
6083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                ContactsProvider2.this.setIsPrimary(newPrimaryId);
6093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
6103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long contactId) {
613e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
614e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
6153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Cursor c = queryData(db, contactId);
6163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
617e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
618e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    long dataId = c.getLong(DataQuery.ID);
619e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    int type = c.getInt(DataQuery.DATA2);
620e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
621e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
622e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
623e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
6243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
6253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
6263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
6273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
628e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
629e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
630e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
631e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
632e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
633e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
634e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
635e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
636e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
6373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long contactId) {
6403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO Lookup integer mimetype IDs' instead of joining for speed
64167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            return db.query(DataQuery.TABLE, DataQuery.COLUMNS, Data.CONTACT_ID + "="
6423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    + contactId + " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
6433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
6443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected void fixContactDisplayName(SQLiteDatabase db, long contactId) {
647e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (!sDisplayNamePriorities.containsKey(mMimetype)) {
648e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                return;
649e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
650e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
6513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String bestDisplayName = null;
65267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Cursor c = db.query(DisplayNameQuery.TABLE, DisplayNameQuery.COLUMNS,
6533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    Data.CONTACT_ID + "=" + contactId, null, null, null, null);
6543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
6553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                int maxPriority = -1;
6563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                while (c.moveToNext()) {
6573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String mimeType = c.getString(DisplayNameQuery.MIMETYPE);
6583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    boolean primary;
6593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String name;
6603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
6623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        name = c.getString(DisplayNameQuery.DISPLAY_NAME);
6633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = true;
6643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    } else {
6653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        name = c.getString(DisplayNameQuery.DATA2);
6663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
6673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
6683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    if (primary && name != null) {
6703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        Integer priority = sDisplayNamePriorities.get(mimeType);
6713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        if (priority != null && priority > maxPriority) {
6723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            maxPriority = priority;
6733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            bestDisplayName = name;
6743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        }
6753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
6763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
6773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
6793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
6803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
6813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            ContactsProvider2.this.setDisplayName(contactId, bestDisplayName);
6833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
6853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
6873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
6893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
6903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
6923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
6943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final NameSplitter mNameSplitter;
6963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public StructuredNameRowHandler(NameSplitter nameSplitter) {
6983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
6993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mNameSplitter = nameSplitter;
7003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
7043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            fixStructuredNameComponents(values);
705e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return super.insert(db, contactId, values);
7063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
7103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO Parse the full name if it has changed and replace pre-existing piece parts.
7113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
7143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Parses the supplied display name, but only if the incoming values do not already contain
7153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * structured name parts.  Also, if the display name is not provided, generate one by
7163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * concatenating first name and last name
7173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         *
7183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * TODO see if the order of first and last names needs to be conditionally reversed for
7193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * some locales, e.g. China.
7203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
7213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private void fixStructuredNameComponents(ContentValues values) {
7223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String fullName = values.getAsString(StructuredName.DISPLAY_NAME);
7233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (!TextUtils.isEmpty(fullName)
7243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.PREFIX))
7253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.GIVEN_NAME))
7263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.MIDDLE_NAME))
7273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.FAMILY_NAME))
7283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.SUFFIX))) {
7293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
7303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                mNameSplitter.split(name, fullName);
7313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.PREFIX, name.getPrefix());
7333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.GIVEN_NAME, name.getGivenNames());
7343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.MIDDLE_NAME, name.getMiddleName());
7353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.FAMILY_NAME, name.getFamilyName());
7363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.SUFFIX, name.getSuffix());
7373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (TextUtils.isEmpty(fullName)) {
7403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                String givenName = values.getAsString(StructuredName.GIVEN_NAME);
7413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                String familyName = values.getAsString(StructuredName.FAMILY_NAME);
7423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                if (TextUtils.isEmpty(givenName)) {
7433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = familyName;
7443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                } else if (TextUtils.isEmpty(familyName)) {
7453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = givenName;
7463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                } else {
7473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = givenName + " " + familyName;
7483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
7493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                if (!TextUtils.isEmpty(fullName)) {
7513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    values.put(StructuredName.DISPLAY_NAME, fullName);
7523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
7533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
7563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
7583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
7603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
7613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
7633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
7643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
7653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
7663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
7703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int type;
7713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String label;
7723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (values.containsKey(mTypeColumn)) {
7733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                type = values.getAsInteger(mTypeColumn);
7743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } else {
7753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                type = BaseTypes.TYPE_CUSTOM;
7763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (values.containsKey(mLabelColumn)) {
7783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                label = values.getAsString(mLabelColumn);
7793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } else {
7803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                label = null;
7813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (type != BaseTypes.TYPE_CUSTOM && label != null) {
7843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                throw new RuntimeException(mLabelColumn + " value can only be specified with "
7853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        + mTypeColumn + "=" + BaseTypes.TYPE_CUSTOM + "(custom)");
7863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (type == BaseTypes.TYPE_CUSTOM && label == null) {
7893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                throw new RuntimeException(mLabelColumn + " value must be specified when "
7903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        + mTypeColumn + "=" + BaseTypes.TYPE_CUSTOM + "(custom)");
7913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
793e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return super.insert(db, contactId, values);
7943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
7983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO read the data and check the constraint
7993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
8013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
8033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
8053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
8063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
8103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long id = super.insert(db, contactId, values);
8113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            fixContactDisplayName(db, contactId);
8123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return id;
8133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
8173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
8183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
8193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
8203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
8213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
8223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
8253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
826e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
827e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
828e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
829e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
830e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
831e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
832e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
833e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
834e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long id = super.insert(db, contactId, values);
835e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            fixContactDisplayName(db, contactId);
836e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return id;
837e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
838e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
839e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
840e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
841e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
842e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
843e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
844e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
845e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
846e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
847e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
848e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
849e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
850e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
8513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
8523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
8543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
8553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public long insert(SQLiteDatabase db, long contactId, ContentValues values) {
859e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            ContentValues phoneValues = new ContentValues();
860e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String number = values.getAsString(Phone.NUMBER);
861e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
862e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
863e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
864e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
865e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
866e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
8673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long id = super.insert(db, contactId, values);
8683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
869e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
870e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.CONTACT_ID, contactId);
871e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, id);
872e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
873e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
874e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
8753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return id;
8773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
8813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
8823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
8833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
8843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
8853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
8863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
8873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
8883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
8893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
8903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
8913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
8943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
89653056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
8974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
89831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
899a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    private ContactAggregator mContactAggregator;
9004097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
901f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
902a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
90320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
90420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
905a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
90653056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
907a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
908a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
909a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
910a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
911a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
91253056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
91353056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
914a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
9154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
9164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
918b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        final Context context = getContext();
91935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
92031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        mOpenHelper = getOpenHelper(context);
921d51a83ac4f8032b62d9a23b90a8f43d6b7eb2dbbDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mOpenHelper, this);
92253056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mContactAggregator = new ContactAggregator(context, mOpenHelper, mAggregationScheduler);
923a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
924d51a83ac4f8032b62d9a23b90a8f43d6b7eb2dbbDmitri Plotnikov        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
925c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
926c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_PRIMARY
927c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetPrimaryWhere);
928c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
929c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_SUPER_PRIMARY
930c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetSuperPrimaryWhere);
931f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
932f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                + Contacts.TIMES_CONTACTED + "=" + Contacts.TIMES_CONTACTED + "+1,"
933f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                + Contacts.LAST_TIME_CONTACTED + "=? WHERE " + Contacts.AGGREGATE_ID + "=?");
934a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
9353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mContactDisplayNameUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
9363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                + ContactsColumns.DISPLAY_NAME + "=? WHERE " + Contacts._ID + "=?");
9373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
93828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
93928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
94028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
94128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
94228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_conjunctions));
9434097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
9443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
9453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
946e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
9473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
9483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
94967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
95067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
9513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
9523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
95367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
95467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL));
9553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
9563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
9573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
9594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
96131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
96231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
96331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
96431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
96531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
966a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
967a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
968a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
969a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
970a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
971a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
972a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
973a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
974a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
975a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
976a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
977a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
978a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
979a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
980a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
981a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
982a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
983a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Called when a change has been made.
984a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
985a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param uri the uri that the change was made to
986a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
987a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private void onChange(Uri uri) {
988a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null);
989a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
990a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
9914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean isTemporary() {
9934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return false;
9944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
9963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
9973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
9983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
9993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
10003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
10013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
10033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
10064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Uri insert(Uri uri, ContentValues values) {
1007a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1008a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
100935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1010a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
101135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
101235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                id = mOpenHelper.getSyncState().insert(mOpenHelper.getWritableDatabase(), values);
101335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
101435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
10156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
1016f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                insertAggregate(values);
10176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
10186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
10196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1020a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS: {
1021f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
1022f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                id = insertContact(values, account);
1023a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1024a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1025a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1026a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
1027a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                values.put(Data.CONTACT_ID, uri.getPathSegments().get(1));
1028035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
1029a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1030a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1031a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1033035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
1034a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1035a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1036a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1037ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1038ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
1039ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                id = insertGroup(values, account);
1040ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1041ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1042ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
10431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
10441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
10451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
10461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
10471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1048a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
1049f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1050a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1051a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
10527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
10537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
10547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
10557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final Uri result = ContentUris.withAppendedId(uri, id);
1057a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        onChange(result);
1058a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return result;
1059a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1060a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1061a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1062035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
1063035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
1064035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
1065035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
1066035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
10677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1068035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
1069035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
1070035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountName = values.getAsString(Contacts.ACCOUNT_NAME);
1071035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountType = values.getAsString(Contacts.ACCOUNT_TYPE);
1072035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
1073035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
1074035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
1075035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
1076035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
1077035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
1078035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1079035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
1080035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_NAME, account.mName);
1081035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_TYPE, account.mType);
1082035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1083035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
10847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
10857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
10876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * Inserts an item in the aggregates table
10886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
10896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
10906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
10916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
10926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private long insertAggregate(ContentValues values) {
1093a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregates are created automatically");
10946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
10956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
10966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
1097a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
1098a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1099a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1100f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
1101a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1102a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1103f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private long insertContact(ContentValues values, Account account) {
11046bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        /*
11056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * The contact record is inserted in the contacts table, but it needs to
11066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * be processed by the aggregator before it will be returned by the
11076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * "aggregates" queries.
11086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         */
1109a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
11106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1111a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
1112a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        overriddenValues.putNull(Contacts.AGGREGATE_ID);
1113f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
11147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
11157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
11167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1117f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        long contactId = db.insert(Tables.CONTACTS, Contacts.AGGREGATE_ID, overriddenValues);
1118f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1119f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = Contacts.AGGREGATION_MODE_DEFAULT;
1120f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        if (values.containsKey(Contacts.AGGREGATION_MODE)) {
1121f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            aggregationMode = values.getAsInteger(Contacts.AGGREGATION_MODE);
1122f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
1123a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1124f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        triggerAggregation(contactId, aggregationMode);
1125a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1126f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        return contactId;
1127a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1128a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1129a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1130a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
1131a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1132a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1133a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1134a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1135035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private long insertData(ContentValues values) {
1136f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = Contacts.AGGREGATION_MODE_DISABLED;
1137a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1138a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1139a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
1140a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        db.beginTransaction();
1141a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        try {
114220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.clear();
114320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.putAll(values);
114420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
114520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            long contactId = mValues.getAsLong(Data.CONTACT_ID);
1146a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
114767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // Replace package with internal mapping
114820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            final String packageName = mValues.getAsString(Data.RES_PACKAGE);
114967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            if (packageName != null) {
115020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
115167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            }
115220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
115367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
1154619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // Replace mimetype with internal mapping
115520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            final String mimeType = mValues.getAsString(Data.MIMETYPE);
115620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (TextUtils.isEmpty(mimeType)) {
115720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                throw new RuntimeException(Data.MIMETYPE + " is required");
115820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
115920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
116020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
116120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.MIMETYPE);
1162508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
11633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
116420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                boolean containsGroupSourceId = mValues.containsKey(GroupMembership.GROUP_SOURCE_ID);
116520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                boolean containsGroupId = mValues.containsKey(GroupMembership.GROUP_ROW_ID);
11669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (containsGroupSourceId && containsGroupId) {
11679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalArgumentException(
11689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
11699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                                    + "and GroupMembership.GROUP_ROW_ID");
11709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
11719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
11729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!containsGroupSourceId && !containsGroupId) {
11739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalArgumentException(
11749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
11759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                                    + "and GroupMembership.GROUP_ROW_ID");
11769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
11779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
11789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (containsGroupSourceId) {
117920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    final String sourceId = mValues.getAsString(GroupMembership.GROUP_SOURCE_ID);
11809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    final long groupId = getOrMakeGroup(db, contactId, sourceId);
118120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    mValues.remove(GroupMembership.GROUP_SOURCE_ID);
118220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
11839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
11844097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            }
11854097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
118620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            id = getDataRowHandler(mimeType).insert(db, contactId, mValues);
1187a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1188f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            aggregationMode = mContactAggregator.markContactForAggregation(contactId);
1189a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1190a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.setTransactionSuccessful();
1191a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        } finally {
1192a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.endTransaction();
1193a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1194a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1195f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        triggerAggregation(id, aggregationMode);
1196a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
11974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1199f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private void triggerAggregation(long contactId, int aggregationMode) {
1200f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
1201f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_DEFAULT:
1202f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.schedule();
1203f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1204f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1205f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_IMMEDITATE:
1206f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.aggregateContact(contactId);
1207f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1208f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1209f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_DISABLED:
1210f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                // Do nothing
1211f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1212f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
1213f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
1214f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1215a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
12169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * Returns the group id of the group with sourceId and the same account as contactId.
12179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
12189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
12199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param contactId the contact this group is associated with
12209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
12219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
12229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
12239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
12249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
12259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private long getOrMakeGroup(SQLiteDatabase db, long contactId, String sourceId) {
12269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
122767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, Contacts._ID + "="
122867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + contactId, null, null, null, null);
12299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
12309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
123167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountName = c.getString(ContactsQuery.ACCOUNT_NAME);
123267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountType = c.getString(ContactsQuery.ACCOUNT_TYPE);
12339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
12349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
12359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
12369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
12379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
12389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
12399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
12409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
12419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
12429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
12439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
12449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
12459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
12469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
12479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // as the contact refered to by contactId
12489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        c = db.query(Tables.GROUPS, new String[]{Contacts._ID},
12499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
12509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                new String[]{sourceId, account.mName, account.mType}, null, null, null);
12519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
12529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
12539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
12549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
12559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
12569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.mName);
12579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.mType);
12589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
12599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
12609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
12619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
12629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
12639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
12649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
12659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
12669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
12679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
12689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
12699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
12709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
12719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
127220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
127320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
127420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private int deleteData(String selection, String[] selectionArgs) {
127520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
127620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
127720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        db.beginTransaction();
127820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
127920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
128020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Note that the query will return data according to the access restrictions,
128120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // so we don't need to worry about deleting data we don't have permission to read.
128220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            Cursor c = query(Data.CONTENT_URI, DataIdQuery.COLUMNS, selection, selectionArgs, null);
128320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            try {
128420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                while(c.moveToNext()) {
128520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    long dataId = c.getLong(DataIdQuery._ID);
128620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    count += deleteData(dataId);
128720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
128820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            } finally {
128920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                c.close();
129020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
129120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.setTransactionSuccessful();
129220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
129320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.endTransaction();
129420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
129520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
129620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
129720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
129820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
129920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
130020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
130120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Cursor c = db.query(DataQuery.TABLE, DataQuery.COLUMNS,
130220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
130320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // TODO apply restrictions
130420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
130520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
130620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
130720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
130820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
130920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            String mimeType = c.getString(DataQuery.MIMETYPE);
131020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
131120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
131220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
131320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
131420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
131520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
131620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
131720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
131820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
131920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                throw new RuntimeException("Data type mismatch: expected "
132020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
132120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
132220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
132320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            return getDataRowHandler(mimeType).delete(db, c);
132420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
132520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
132620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
132720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
132820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
132920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
1330ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Delete the given {@link Data} row, fixing up any {@link Aggregates}
1331ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * primaries that reference it.
1332ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1333ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private int deleteData(long dataId) {
1334ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1335ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1336ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimePhone = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
1337ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimeEmail = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1338ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1339ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Check to see if the data about to be deleted was a super-primary on
1340ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // the parent aggregate, and set flags to fix-up once deleted.
1341ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long aggId = -1;
1342ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long mimeId = -1;
1343ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        String dataRaw = null;
1344ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixOptimal = false;
1345ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixFallback = false;
1346ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1347ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor cursor = null;
1348ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        try {
134967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            cursor = db.query(DataAggregatesQuery.TABLE, DataAggregatesQuery.PROJECTION,
135067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
1351ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor.moveToFirst()) {
135267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                aggId = cursor.getLong(DataAggregatesQuery.AGGREGATE_ID);
135367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                mimeId = cursor.getLong(DataAggregatesQuery.MIMETYPE_ID);
1354ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
135567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    dataRaw = cursor.getString(DataAggregatesQuery.PHONE_NUMBER);
135667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixOptimal = (cursor.getLong(DataAggregatesQuery.OPTIMAL_PHONE_ID) == dataId);
135767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixFallback = (cursor.getLong(DataAggregatesQuery.FALLBACK_PHONE_ID) == dataId);
1358ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
135967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    dataRaw = cursor.getString(DataAggregatesQuery.EMAIL_DATA);
136067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixOptimal = (cursor.getLong(DataAggregatesQuery.OPTIMAL_EMAIL_ID) == dataId);
136167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    fixFallback = (cursor.getLong(DataAggregatesQuery.FALLBACK_EMAIL_ID) == dataId);
1362ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1363ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1364ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        } finally {
1365ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor != null) {
1366ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.close();
1367ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor = null;
1368ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1370ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Delete the requested data item.
1372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        int dataDeleted = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
1373ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1374ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Fix-up any super-primary values that are now invalid.
1375ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (fixOptimal || fixFallback) {
1376ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final ContentValues values = new ContentValues();
1377ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final StringBuilder scoreClause = new StringBuilder();
1378ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1379ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String SCORE = "score";
1380ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1381ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Build scoring clause that will first pick data items under the
1382ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // same aggregate that have identical values, otherwise fall back to
1383ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // normal primary scoring from the member contacts.
1384ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("(CASE WHEN ");
1385ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (mimeId == mimePhone) {
1386ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Phone.NUMBER);
1387ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            } else if (mimeId == mimeEmail) {
1388ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Email.DATA);
1389ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1390ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("=");
1391ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            DatabaseUtils.appendEscapedSQLString(scoreClause, dataRaw);
1392ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append(" THEN 2 ELSE " + Data.IS_PRIMARY + " END) AS " + SCORE);
1393ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1394ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String[] PROJ_PRIMARY = new String[] {
1395ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    DataColumns.CONCRETE_ID,
1396ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Contacts.IS_RESTRICTED,
1397ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    scoreClause.toString(),
1398ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            };
1399ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1400ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_DATA_ID = 0;
1401ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_IS_RESTRICTED = 1;
140267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            final int COL_SCORE = 2;
1403ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1404ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES, PROJ_PRIMARY,
1405ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND " + DataColumns.MIMETYPE_ID
1406ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            + "=" + mimeId, null, null, null, SCORE);
1407ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1408ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixOptimal) {
1409ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
141067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                String colIsRestricted = null;
1411ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
1412ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID;
141367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    colIsRestricted = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_IS_RESTRICTED;
1414ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
1415ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID;
141667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    colIsRestricted = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_IS_RESTRICTED;
1417ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1418ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1419ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixOptimal told us that
1420ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
1421ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
142267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                values.putNull(colIsRestricted);
1423ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // When finding a new optimal primary, we only care about the
1425ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // highest scoring value, regardless of source.
1426ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (cursor.moveToFirst()) {
1427ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimal = cursor.getLong(COL_DATA_ID);
142867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    final long newIsRestricted = cursor.getLong(COL_IS_RESTRICTED);
1429ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1430ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimal != 0) {
1431ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, newOptimal);
1432ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
143367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    if (newIsRestricted != 0) {
143467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                        values.put(colIsRestricted, newIsRestricted);
1435ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
1436ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1437ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1438ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1439ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixFallback) {
1440ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
1441ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
1442ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID;
1443ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
1444ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID;
1445ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1446ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1447ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixFallback told us that
1448ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
1449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
1450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1451ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // The best fallback value is the highest scoring data item that
1452ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // hasn't been restricted.
1453ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.moveToPosition(-1);
1454ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                while (cursor.moveToNext()) {
1455ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final boolean isRestricted = (cursor.getInt(COL_IS_RESTRICTED) == 1);
1456ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (!isRestricted) {
1457ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, cursor.getLong(COL_DATA_ID));
1458ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        break;
1459ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
1460ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1461ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1462ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1463ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Push through any aggregate updates we have
1464ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (values.size() > 0) {
1465ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                db.update(Tables.AGGREGATES, values, AggregatesColumns.CONCRETE_ID + "=" + aggId,
1466ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        null);
1467ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1468ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1469ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1470ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return dataDeleted;
1471ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1472ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1473ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
1474ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
1475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1476ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private long insertGroup(ContentValues values, Account account) {
1477ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1478ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1479ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
1480ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
1481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
1482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1483ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1484ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
148567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE);
148667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
148767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            overriddenValues.put(GroupsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
148867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
148967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        overriddenValues.remove(Groups.RES_PACKAGE);
1490ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1491ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return db.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
1492ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1493ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1494ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
14951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
14961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
149770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public long insertPresence(ContentValues values) {
14981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
14991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
15001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String protocol = values.getAsString(Presence.IM_PROTOCOL);
15011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(handle) || TextUtils.isEmpty(protocol)) {
15021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            throw new IllegalArgumentException("IM_PROTOCOL and IM_HANDLE are required");
15031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
15041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
15051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
15061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        boolean matchEmail = GTALK_PROTOCOL_STRING.equals(protocol);
15071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
150870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        StringBuilder selection = new StringBuilder();
15091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
15101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
151170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append("(" + Clauses.WHERE_IM_MATCHES + ") OR ("
151270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    + Clauses.WHERE_EMAIL_MATCHES + ")");
15131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle, handle };
15141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
151570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append(Clauses.WHERE_IM_MATCHES);
15161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle };
15171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
15181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
151970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (values.containsKey(Presence.DATA_ID)) {
152070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append(" AND " + DataColumns.CONCRETE_ID + "=")
152170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    .append(values.getAsLong(Presence.DATA_ID));
152270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
152370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
152470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (values.containsKey(Presence.CONTACT_ID)) {
152570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append(" AND " + DataColumns.CONCRETE_CONTACT_ID + "=")
152670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    .append(values.getAsLong(Presence.CONTACT_ID));
152770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
152870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
152970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        selection.append(" AND ").append(getContactsRestrictionExceptions());
153070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
15311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
1532c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        long contactId = -1;
153370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
15341f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
15351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
153670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            cursor = db.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
153770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    selection.toString(), selectionArgs, null, null, null);
15381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
153967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
1540c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
15411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
15421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
15431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
15441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
15451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
154631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
154731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
154831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
15491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
15501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
15511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.DATA_ID, dataId);
1552c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        values.put(Presence.CONTACT_ID, contactId);
15531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
15541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Insert the presence update
15551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long presenceId = db.replace(Tables.PRESENCE, null, values);
15561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return presenceId;
15571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
15581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
15594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
15604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int delete(Uri uri, String selection, String[] selectionArgs) {
1561508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1562508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
1563508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
156435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
156535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().delete(db, selection, selectionArgs);
156635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
15676bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: {
15686bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                long aggregateId = ContentUris.parseId(uri);
15696bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
15706bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                // Remove references to the aggregate first
15716bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                ContentValues values = new ContentValues();
15726bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                values.putNull(Contacts.AGGREGATE_ID);
1573b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.update(Tables.CONTACTS, values, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
15746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1575b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return db.delete(Tables.AGGREGATES, BaseColumns._ID + "=" + aggregateId, null);
15766bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
15776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1578508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case CONTACTS_ID: {
1579508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long contactId = ContentUris.parseId(uri);
1580b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int contactsDeleted = db.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
1581b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, Data.CONTACT_ID + "=" + contactId, null);
1582508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                return contactsDeleted + dataDeleted;
1583508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1584508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
158520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
158620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return deleteData(selection, selectionArgs);
158720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
158820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1589508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID: {
1590508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
1591ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return deleteData(dataId);
1592ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1593ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1594ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1595ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1596ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final long groupMembershipMimetypeId = mOpenHelper
1597ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1598ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int groupsDeleted = db.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
1599ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
1600ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
1601ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupId, null);
1602ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1603ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return groupsDeleted + dataDeleted;
1604508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1605508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
16061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
16071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return db.delete(Tables.PRESENCE, null, null);
16081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
16091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1610508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            default:
16113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
1612508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
16134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
16144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1615f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private static Account readAccountFromQueryParams(Uri uri) {
1616035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String name = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1617035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String type = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1618f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
1619f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            return null;
1620f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        }
1621f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        return new Account(name, type);
1622f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    }
1623f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
1624ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
16254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
16264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
162700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
162835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
162900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
163000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
163100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
163235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
163335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().update(db, values, selection, selectionArgs);
163435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1635c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            // TODO(emillar): We will want to disallow editing the aggregates table at some point.
163600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES: {
163700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                count = db.update(Tables.AGGREGATES, values, selection, selectionArgs);
163800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
163900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
164000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
164100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES_ID: {
1642d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                count = updateAggregateData(db, ContentUris.parseId(uri), values);
1643c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
1644c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
1645c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
164620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
164720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                count = updateData(values, selection, selectionArgs);
164820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
164920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
1650c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
165120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA_ID: {
165220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                count = updateData(ContentUris.parseId(uri), values);
165300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
165400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
16557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS: {
16577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selection, selectionArgs);
16587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
16597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
16607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID: {
16627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selectionWithId = (Contacts._ID + " = " + ContentUris.parseId(uri) + " ")
16637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        + (selection == null ? "" : " AND " + selection);
16647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selectionWithId, selectionArgs);
16657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Log.i(TAG, "Selection is: " + selectionWithId);
16667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
16677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
16687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1669ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1670ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selection, selectionArgs);
1671ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1672ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1673ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1674ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1675ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1676ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1677ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String selectionWithId = (Groups._ID + "=" + groupId + " ")
1678ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + (selection == null ? "" : " AND " + selection);
1679ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selectionWithId, selectionArgs);
1680ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1681ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // If changing visibility, then update aggregates
1682ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (values.containsKey(Groups.GROUP_VISIBLE)) {
1683ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    mOpenHelper.updateAllVisible();
1684ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1685ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1686ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1687ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1688ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1689127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1690127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                count = updateAggregationException(db, values);
1691b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1692b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1693b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
16947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
1695f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
169600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
169700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
169800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        if (count > 0) {
169900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            getContext().getContentResolver().notifyChange(uri, null);
170000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
170100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
17024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
17034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
170420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private int updateData(ContentValues values, String selection,
170520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            String[] selectionArgs) {
170620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
170720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
170820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        db.beginTransaction();
170920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
171020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
171120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Note that the query will return data according to the access restrictions,
171220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // so we don't need to worry about deleting data we don't have permission to read.
171320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            Cursor c = query(Data.CONTENT_URI, DataIdQuery.COLUMNS, selection, selectionArgs, null);
171420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            try {
171520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                while(c.moveToNext()) {
171620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    long dataId = c.getLong(DataIdQuery._ID);
171720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    count += updateData(dataId, values);
171820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
171920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            } finally {
172020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                c.close();
172120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
172220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.setTransactionSuccessful();
172320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
172420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.endTransaction();
172520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
172620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
172720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
172820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
172920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
173020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private int updateData(long dataId, ContentValues values) {
173120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
173220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
173320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
173420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
173520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
173620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.CONTACT_ID);
173720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
173820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
173920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
174020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
174120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
174220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
174320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
174420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
174570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
174670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
174720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
174820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
174920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
175070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
175120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
175270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
175320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
175470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
175520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
175670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
175720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
175820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
175920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (containsIsSuperPrimary) {
176020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            setIsSuperPrimary(dataId);
176120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            setIsPrimary(dataId);
176220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
176320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Now that we've taken care of setting these, remove them from "values".
176470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
176520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (containsIsPrimary) {
176670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                mValues.remove(Data.IS_PRIMARY);
176720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
176820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } else if (containsIsPrimary) {
176920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            setIsPrimary(dataId);
177020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
177120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Now that we've taken care of setting this, remove it from "values".
177270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
177320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
177420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
177570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (mValues.size() > 0) {
177670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return db.update(Tables.DATA, mValues, Data._ID + " = " + dataId, null);
177720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
177820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return 0;
177920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
178020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1781d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private int updateAggregateData(SQLiteDatabase db, long aggregateId, ContentValues values) {
1782d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1783d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // First update all constituent contacts
1784f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        ContentValues optionValues = new ContentValues(5);
1785f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyStringValue(optionValues, Contacts.CUSTOM_RINGTONE,
1786f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.CUSTOM_RINGTONE);
1787f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.SEND_TO_VOICEMAIL,
1788f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.SEND_TO_VOICEMAIL);
1789f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.LAST_TIME_CONTACTED,
1790f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.LAST_TIME_CONTACTED);
1791f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.TIMES_CONTACTED,
1792f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.TIMES_CONTACTED);
1793f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.STARRED,
1794f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.STARRED);
1795d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1796d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
1797d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (optionValues.size() == 0) {
1798d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
1799d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1800d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1801f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        db.update(Tables.CONTACTS, optionValues, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
1802f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        return db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggregateId, null);
1803f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
1804d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1805f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    public void updateContactTime(long aggregateId, long lastTimeContacted) {
1806f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
1807f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(2, aggregateId);
1808f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.execute();
1809d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1810d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1811127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private static class ContactPair {
1812127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId1;
1813127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId2;
1814127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1815127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        /**
1816127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         * Constructor that ensures that this.contactId1 &lt; this.contactId2
1817127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         */
1818127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public ContactPair(long contactId1, long contactId2) {
1819127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (contactId1 < contactId2) {
1820127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId1;
1821127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId2;
1822127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1823127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId1;
1824127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId2;
1825127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1826127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1827127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    }
182880c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1829127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
1830127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
1831127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long aggregateId = values.getAsInteger(AggregationExceptions.AGGREGATE_ID);
1832127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long contactId = values.getAsInteger(AggregationExceptions.CONTACT_ID);
183380c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1834127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // First, we build a list of contactID-contactID pairs for the given aggregate and contact.
1835127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ArrayList<ContactPair> pairs = new ArrayList<ContactPair>();
183667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, Contacts.AGGREGATE_ID
183767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + "=" + aggregateId, null, null, null, null);
1838127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        try {
1839127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            while (c.moveToNext()) {
184067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                long aggregatedContactId = c.getLong(ContactsQuery.CONTACT_ID);
1841e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                if (aggregatedContactId != contactId) {
1842e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                    pairs.add(new ContactPair(aggregatedContactId, contactId));
1843e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                }
1844b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1845b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        } finally {
1846b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            c.close();
1847b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
1848127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1849127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // Now we iterate through all contact pairs to see if we need to insert/delete/update
1850127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // the corresponding exception
1851127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ContentValues exceptionValues = new ContentValues(3);
1852127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
1853127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        for (ContactPair pair : pairs) {
1854127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            final String whereClause =
1855127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    AggregationExceptionColumns.CONTACT_ID1 + "=" + pair.contactId1 + " AND "
1856127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    + AggregationExceptionColumns.CONTACT_ID2 + "=" + pair.contactId2;
1857127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
1858127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.delete(Tables.AGGREGATION_EXCEPTIONS, whereClause, null);
1859127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1860127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID1, pair.contactId1);
1861127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID2, pair.contactId2);
1862127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
1863127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        exceptionValues);
1864127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1865127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1866127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1867f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = mContactAggregator.markContactForAggregation(contactId);
1868f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        if (aggregationMode != Contacts.AGGREGATION_MODE_DISABLED) {
1869f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            mContactAggregator.aggregateContact(db, contactId);
1870f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC
1871f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                    || exceptionType == AggregationExceptions.TYPE_KEEP_OUT) {
1872f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.updateAggregateData(aggregateId);
1873f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            }
18747a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        }
1875127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1876127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
1877127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
1878127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
1879b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
1880b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1881619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1882619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list.
1883619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1884619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private boolean isContained(String[] array, String value) {
1885bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array != null) {
1886bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            for (String test : array) {
1887bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                if (value.equals(test)) {
1888bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                    return true;
1889bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                }
1890619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1891619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1892619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return false;
1893619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1894619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1895619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1896619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list, and add to the
1897619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * array if the value doesn't already appear.
1898619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1899619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private String[] assertContained(String[] array, String value) {
1900bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array == null) {
1901bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            array = new String[] {value};
1902bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        } else if (!isContained(array, value)) {
1903619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            String[] newArray = new String[array.length + 1];
1904619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            System.arraycopy(array, 0, newArray, 0, array.length);
1905619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            newArray[array.length] = value;
1906619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            array = newArray;
1907619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1908619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return array;
1909619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1910619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
19114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
19124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
19134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
19144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
191535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1916d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
19171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
19181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String limit = null;
1919bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        String aggregateIdColName = Tables.AGGREGATES + "." + Aggregates._ID;
19204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1921619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
1922619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
1923a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
19244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
192535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
192635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
192735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
192835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
19296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
1930b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1931619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1932619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
1933619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sAggregatesProjectionMap);
1934619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1935619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1936619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1937619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case AGGREGATES_ID: {
1938619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = ContentUris.parseId(uri);
1939619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1940ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1941619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1942619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
19436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                qb.setProjectionMap(sAggregatesProjectionMap);
19446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
19456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
19466bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
19471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY: {
1948619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
19491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1950619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1951619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1952619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
19531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1954bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
19551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
19561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
19571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
19581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY_ID: {
1959619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
19601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                long aggId = ContentUris.parseId(uri);
19611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1962ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1963619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1964619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1965619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
19661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1967bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
19681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
19691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
19701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1971ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case AGGREGATES_SUMMARY_FILTER: {
1972619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1973ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1974ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1975ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1976ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1977ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1978bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
1979ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1980ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1981ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1982d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT_FILTER:
1983d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT: {
1984d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the first query for starred
1985d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1986d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1987d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1988d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1989d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1990d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1991d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String starredQuery = qb.buildQuery(projection, Aggregates.STARRED + "=1",
1992bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null,
1993d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        null /* limit */);
1994d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1995d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
1996d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
1997d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1998d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1999d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
2000d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
2001d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
2002d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
2003d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
2004d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Aggregates.TIMES_CONTACTED + " > 0 AND (" + Aggregates.STARRED
2005d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        + " = 0 OR " + Aggregates.STARRED + " IS NULL)",
2006bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null, null);
2007d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
2008d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
2009d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
2010d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
2011d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                Cursor c = db.rawQueryWithFactory(null, query, null,
2012d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
2013d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
2014d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if ((c != null) && !isTemporary()) {
2015d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
2016d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
2017d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
2018d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
2019d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
2020d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
2021b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            case AGGREGATES_SUMMARY_GROUP: {
2022b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
2023b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregateRestrictionExceptions(qb);
2024b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
2025b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
2026b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
2027b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
2028b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    qb.appendWhere(" AND " + sAggregatesInGroupSelect);
2029b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    selectionArgs = appendGroupArg(selectionArgs, uri.getLastPathSegment());
2030b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
2031b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                groupBy = aggregateIdColName;
2032b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
2033b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
2034b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
20356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_DATA: {
2036619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = Long.parseLong(uri.getPathSegments().get(1));
203767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES_GROUPS);
20389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataContactsGroupsAggregateProjectionMap);
2039619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Contacts.AGGREGATE_ID + "=" + aggId + " AND ");
2040619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
20416bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20426bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
204300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2044ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
204567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
2046ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
2047ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
2048ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
2049ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(" AND " + buildAggregateLookupWhereClause(
2050ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                            uri.getLastPathSegment()));
2051ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
2052ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2053ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2054ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2055ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES: {
205667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
2057ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
2058ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Phone.CONTENT_ITEM_TYPE + "\"");
2059ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2060ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2061ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2062ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
206367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
2064ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
206567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.appendWhere(Data.MIMETYPE + " = \"" + StructuredPostal.CONTENT_ITEM_TYPE + "\"");
2066ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2067ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2068ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
20694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: {
207067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.CONTACTS);
20714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
2072619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
20734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
20744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
20754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
20764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: {
2077619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = ContentUris.parseId(uri);
207867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.CONTACTS);
20794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
2080ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(ContactsColumns.CONCRETE_ID + "=" + contactId + " AND ");
2081619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
20824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
20834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
20844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2085a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
2086619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = Long.parseLong(uri.getPathSegments().get(1));
208767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
20889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataContactsGroupsProjectionMap);
2089619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Data.CONTACT_ID + "=" + contactId + " AND ");
2090619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
2091a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2092a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2093a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
209428ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey            case CONTACTS_FILTER_EMAIL: {
2095619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
209667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_AGGREGATES);
2097e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.setProjectionMap(sDataContactsProjectionMap);
20985d0f923eb4c5351ebf323cc6f19c82acff98693eJeff Sharkey                qb.appendWhere(Data.MIMETYPE + "='" + CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'");
2099e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhere(" AND " + CommonDataKinds.Email.DATA + "=");
2100e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhereEscapeString(uri.getPathSegments().get(2));
2101e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
2102e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
2103e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
2104e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
2105035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
2106035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
2107343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                if (!TextUtils.isEmpty(accountName)) {
2108035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                    qb.appendWhere(Contacts.ACCOUNT_NAME + "="
2109035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountName) + " AND "
2110035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + Contacts.ACCOUNT_TYPE + "="
2111035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountType) + " AND ");
2112343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                }
211367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
21149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
2115619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
2116e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
2117e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
2118e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
21194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
212067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
21219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
2122ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri) + " AND ");
2123619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
21244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
21254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
21264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2127a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
2128619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
2129a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
2130a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
2131a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
2132e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                    sortOrder = Data.CONTACT_ID;
2133a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
2134a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2135a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                final String number = uri.getLastPathSegment();
2136bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                OpenHelper.buildPhoneLookupQuery(qb, number);
2137a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                qb.setProjectionMap(sDataContactsProjectionMap);
2138a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2139a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2140a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2141ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2142ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
2143ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
2144ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2145ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2147ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2148ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
2149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
2150ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
2151ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(GroupsColumns.CONCRETE_ID + "=" + groupId);
2152ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2153ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2154ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2155ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
2156ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES_DATA_CONTACTS_AGGREGATES);
2157ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
2158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                groupBy = GroupsColumns.CONCRETE_ID;
2159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2161ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2162b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2163127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS_JOIN_CONTACTS);
2164b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
2165b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2166b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2167b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
216831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
216931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                long aggregateId = Long.parseLong(uri.getPathSegments().get(1));
217031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final String maxSuggestionsParam =
217131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        uri.getQueryParameter(AggregationSuggestions.MAX_SUGGESTIONS);
217231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
217331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
217431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                if (maxSuggestionsParam != null) {
217531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(maxSuggestionsParam);
217631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
217731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
217831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
217931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
218031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(aggregateId, projection,
218131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        sAggregatesProjectionMap, maxSuggestions);
218231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
218331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
21844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
2185f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
2186f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                        sortOrder);
21874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
21884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
21894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Perform the query and set the notification uri
21901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final Cursor c = qb.query(db, projection, selection, selectionArgs,
2191bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy, null, sortOrder, limit);
21924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
21934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
21944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
21954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
21964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
21974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
21987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
219967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * List of package names with access to {@link Contacts#IS_RESTRICTED} data.
220067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     */
220167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private static final String[] sAllowedPackages = new String[] {
220267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        "com.android.contacts",
220367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        "com.facebook",
220467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    };
220567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
220667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    /**
220767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * Check if {@link Binder#getCallingUid()} should be allowed access to
220867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * {@link Contacts#IS_RESTRICTED} data.
220967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     */
221067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private boolean hasRestrictedAccess() {
221167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final PackageManager pm = getContext().getPackageManager();
221267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String[] callerPackages = pm.getPackagesForUid(Binder.getCallingUid());
221367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
221467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        // Has restricted access if caller matches any packages
221567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        for (String callerPackage : callerPackages) {
221667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            for (String allowedPackage : sAllowedPackages) {
221767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                if (allowedPackage.equals(callerPackage)) {
221867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    return true;
221967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                }
222067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            }
222167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
222267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        return false;
222367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
222467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
222567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    /**
2226619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Restrict selection of {@link Aggregates} to only public ones, or those
222767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * the caller has been granted an exception to.
2228619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2229619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregateRestrictionExceptions(SQLiteQueryBuilder qb) {
223067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
223167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.appendWhere("1");
223267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
223367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.appendWhere(AggregatesColumns.SINGLE_IS_RESTRICTED + "=0");
2234619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2235619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2236619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2237619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2238619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the calling process, and
2239619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * add projections to correctly select {@link Aggregates#PRIMARY_PHONE_ID}
2240619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * and {@link Aggregates#PRIMARY_EMAIL_ID}.
2241619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2242619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregatePrimaryRestrictionExceptions(HashMap<String, String> projection) {
224367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        String projectionPhone;
224467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        String projectionEmail;
224567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
224667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
224767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // With restricted access, always give optimal values
224867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionPhone = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID + " AS "
224967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_PHONE_ID;
225067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionEmail = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID + " AS "
225167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_EMAIL_ID;
225267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
225367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // With general access, always give fallback values
225467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionPhone = AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID + " AS "
225567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_PHONE_ID;
225667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            projectionEmail = AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID + " AS "
225767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    + Aggregates.PRIMARY_EMAIL_ID;
225867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
225967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2260619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_PHONE_ID);
2261619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_PHONE_ID, projectionPhone);
2262619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2263619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_EMAIL_ID);
2264619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_EMAIL_ID, projectionEmail);
2265619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2266619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2267619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2268619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
2269619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
2270619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
2271619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2272619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyContactsRestrictionExceptions(SQLiteQueryBuilder qb) {
227370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        qb.appendWhere(getContactsRestrictionExceptions());
227470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
227570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
227670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    private String getContactsRestrictionExceptions() {
227767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
227870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
227970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
228070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return Contacts.IS_RESTRICTED + "=0";
228170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
228270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
228370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
228470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
228570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (hasRestrictedAccess()) {
228670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
228767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
228870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "(SELECT " + Contacts.IS_RESTRICTED + " FROM " + Tables.CONTACTS + " WHERE "
228970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    + ContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
2290619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2291619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2292619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2293619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2294619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
2295619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
2296619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
2297619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2298e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    void applyDataRestrictionExceptions(SQLiteQueryBuilder qb) {
2299619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        applyContactsRestrictionExceptions(qb);
2300619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2301619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2302619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
23037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
23047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
23057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
23067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    private static class ContactsEntityIterator implements EntityIterator {
23077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
23087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
23097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
23117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data1",
23127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data2",
23137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data3",
23147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data4",
23157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data5",
23167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data6",
23177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data7",
23187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data8",
23197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data9",
232067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data10",
232167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data11",
232267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data12",
232367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data13",
232467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data14",
232567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                "data15"};
23267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
2328035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_NAME,
2329035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_TYPE,
2330035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.SOURCE_ID,
2331035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.VERSION,
2332035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.DIRTY,
2333035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data._ID,
233467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.RES_PACKAGE,
2335035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.MIMETYPE,
2336035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA1,
2337035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA2,
2338035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA3,
2339035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA4,
2340035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA5,
2341035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA6,
2342035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA7,
2343035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA8,
2344035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA9,
2345035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA10,
234667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA11,
234767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA12,
234867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA13,
234967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA14,
235067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Contacts.Data.DATA15,
2351035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.CONTACT_ID,
2352035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.IS_PRIMARY,
23539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Contacts.Data.DATA_VERSION,
23549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_SOURCE_ID};
2355035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
2356035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
2357035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
2358035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
2359035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
2360035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
2361035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
236267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
236367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
236467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
236567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_CONTACT_ID = 23;
236667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_IS_PRIMARY = 24;
236767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA_VERSION = 25;
236867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_GROUP_SOURCE_ID = 26;
23697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public ContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
23717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
23727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
23737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
23757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    ? Contacts.Data.CONTACT_ID
23767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    : (Contacts.Data.CONTACT_ID + "," + sortOrder);
23777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
23797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
238067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS);
23819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            qb.setProjectionMap(sDataContactsGroupsProjectionMap);
23827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
23837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                qb.appendWhere(Data.CONTACT_ID + "=" + contactsIdString);
23847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
2385035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
2386035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
2387035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
2388035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.appendWhere(Contacts.ACCOUNT_NAME + "="
2389035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
2390035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + Contacts.ACCOUNT_TYPE + "="
2391035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
2392035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
23937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
23947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
23957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
23967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
23977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
23987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
23997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
24007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
24017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
24037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
24047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
24077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
24087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
24097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
24127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
24157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
24167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
24177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
24197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
24207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
24217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
24237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final long contactId = c.getLong(COLUMN_CONTACT_ID);
24257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
24277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
2428035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
2429035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
24307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts._ID, contactId);
24317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.DIRTY, c.getLong(COLUMN_DIRTY));
2432f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            contactValues.put(Contacts.VERSION, c.getLong(COLUMN_VERSION));
24337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
24347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
24357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
24377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
24387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (contactId != c.getLong(COLUMN_CONTACT_ID)) {
24397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
24407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
24417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
24427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
24437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                dataValues.put(Contacts.Data._ID, c.getString(COLUMN_DATA_ID));
244467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataValues.put(Contacts.Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
2445f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
2446f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.IS_PRIMARY, c.getString(COLUMN_IS_PRIMARY));
2447f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
24489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
24499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
24509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
24519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
24529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
24537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                for (int i = 0; i < 10; i++) {
24547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
24557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
24567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
24577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
24587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
24597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
24607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
24617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
24627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
24637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
24647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
24657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
24667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
24677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
24687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
24697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
24707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
24727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
24747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2475a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
24767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
24777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
24787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
24797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
24807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS:
24817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID:
24827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
24837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (match == CONTACTS_ID) {
24847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
24857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
24867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                return new ContactsEntityIterator(this, contactsIdString,
24887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
24897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
24907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
24917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
24937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
24944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
24954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
2496a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
24974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
24986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: return Aggregates.CONTENT_TYPE;
24996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: return Aggregates.CONTENT_ITEM_TYPE;
25004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: return Contacts.CONTENT_TYPE;
25014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: return Contacts.CONTENT_ITEM_TYPE;
2502508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
25036bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2504508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2505b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return mOpenHelper.getDataMimeType(dataId);
250631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: return AggregationExceptions.CONTENT_TYPE;
250731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTION_ID: return AggregationExceptions.CONTENT_ITEM_TYPE;
250831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: return Aggregates.CONTENT_TYPE;
25094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
2510a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        throw new UnsupportedOperationException("Unknown uri: " + uri);
25114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
25127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2513b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    @Override
25147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
25157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            throws OperationApplicationException {
25167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
25187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        db.beginTransaction();
25197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        try {
25207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentProviderResult[] results = super.applyBatch(operations);
25217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.setTransactionSuccessful();
25227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return results;
25237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        } finally {
25247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.endTransaction();
25257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
25267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
2527c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
25283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private void setDisplayName(long contactId, String displayName) {
25293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
25303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mContactDisplayNameUpdate.bindString(1, displayName);
25313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
25323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mContactDisplayNameUpdate.bindNull(1);
25333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
25343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mContactDisplayNameUpdate.bindLong(2, contactId);
25353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mContactDisplayNameUpdate.execute();
25363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
25373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2538c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
2539c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
2540c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
2541c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
2542c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
2543c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
2544c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsPrimary(long dataId) {
2545c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
2546c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(2, dataId);
2547c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(3, dataId);
2548c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
2549c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
2550c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
2551c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
2552c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
2553c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
2554c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
2555c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
2556c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
2557c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsSuperPrimary(long dataId) {
2558c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
2559c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(2, dataId);
2560c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(3, dataId);
2561c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
2562619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2563619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the parent aggregate and package for this new primary
2564619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2565619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2566619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long aggId = -1;
2567619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isRestricted = false;
2568619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        String mimeType = null;
2569619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2570619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        Cursor cursor = null;
2571619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        try {
257267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            cursor = db.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
257367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
2574619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor.moveToFirst()) {
257567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                aggId = cursor.getLong(DataContactsQuery.AGGREGATE_ID);
257667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                isRestricted = (cursor.getInt(DataContactsQuery.IS_RESTRICTED) == 1);
257767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                mimeType = cursor.getString(DataContactsQuery.MIMETYPE);
2578619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2579619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } finally {
2580619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor != null) {
2581619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                cursor.close();
2582619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2583619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2584619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2585619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Bypass aggregate update if no parent found, or if we don't keep track
2586619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // of super-primary for this mimetype.
2587d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (aggId == -1) {
2588d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return;
2589d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2590619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2591619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isPhone = CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
2592619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isEmail = CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimeType);
2593619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2594619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Record this value as the new primary for the parent aggregate
2595619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final ContentValues values = new ContentValues();
2596619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (isPhone) {
2597619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID, dataId);
259867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_IS_RESTRICTED, isRestricted);
2599619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (isEmail) {
2600619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID, dataId);
260167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_IS_RESTRICTED, isRestricted);
2602619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2603619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2604619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // If this data is unrestricted, then also set as fallback
2605619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (!isRestricted && isPhone) {
2606619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID, dataId);
2607619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (!isRestricted && isEmail) {
2608619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID, dataId);
2609619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2610619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2611619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Push update into aggregates table, if needed
2612619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (values.size() > 0) {
2613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggId, null);
2614619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2615619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2616c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
2617ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2618ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private String buildAggregateLookupWhereClause(String filterParam) {
2619ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        StringBuilder filter = new StringBuilder();
2620ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.AGGREGATES);
2621ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(".");
2622ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Aggregates._ID);
2623ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" IN (SELECT ");
2624ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts.AGGREGATE_ID);
2625ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" FROM ");
2626ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.CONTACTS);
2627ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" WHERE ");
2628ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts._ID);
2629d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(" IN (SELECT  contact_id FROM name_lookup WHERE normalized_name GLOB '");
2630ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // NOTE: Query parameters won't work here since the SQL compiler
2631ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // needs to parse the actual string to know that it can use the
2632ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // index to do a prefix scan.
2633d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(NameNormalizer.normalize(filterParam) + "*");
2634d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append("'))");
2635ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        return filter.toString();
2636ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
2637ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2638b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private String[] appendGroupArg(String[] selectionArgs, String arg) {
2639b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
2640b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
2641b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
2642b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
2643b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
2644b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            System.arraycopy(selectionArgs, 0, newSelectionArgs, 0, selectionArgs.length);
2645b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            newSelectionArgs[newLength - 1] = arg;
2646b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
2647b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
2648b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
26494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
2650