ContactsProvider2.java revision 7a4550f2afb24b2112b6c937f416c6f46ece35f4
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.AggregationExceptionColumns;
2128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Clauses;
22d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikovimport com.android.providers.contacts.OpenHelper.ContactsColumns;
2328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.DataColumns;
2428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.GroupsColumns;
2528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.MimetypesColumns;
2667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport com.android.providers.contacts.OpenHelper.PackagesColumns;
27d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PhoneColumns;
2828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
29d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikovimport com.android.providers.contacts.OpenHelper.RawContactsColumns;
3028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Tables;
31e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikovimport com.google.android.collect.Lists;
32e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
33b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
34c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.content.ContentProvider;
3667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentProviderOperation;
3767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentProviderResult;
3835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
3967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
4067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
4135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
4267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.EntityIterator;
437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.OperationApplicationException;
443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
4567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences.Editor;
4767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.pm.PackageManager;
484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
49ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
50b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
53c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
55619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.os.Binder;
56b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
58508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
59de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract;
603cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovimport android.provider.Contacts.People;
61b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
62de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds;
63d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikovimport android.provider.ContactsContract.Contacts;
64de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Data;
65ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.Groups;
661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkeyimport android.provider.ContactsContract.Presence;
67d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
69ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
70ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
74de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
754097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
7667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
77d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
78a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
79a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
80c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
83b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
89035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintanapublic class ContactsProvider2 extends ContentProvider {
90b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    // TODO: clean up debug tag and rename this class
91b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private static final String TAG = "ContactsProvider ~~~~";
924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
93619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
94619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
95619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
993d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
1003d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * Shared preference key for the legacy contact import version. The need for a version
1013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1023d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
1043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final String PREF_CONTACTS_IMPORTED = "contacts_imported_v1";
1053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int PREF_CONTACTS_IMPORT_VERSION = 1;
1063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
107a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
109d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
110d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.TIMES_CONTACTED + " DESC, "
111d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.DISPLAY_NAME + " ASC";
112d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
113d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
114d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
115d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
116d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
117d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
118d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_DATA = 1002;
119d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_SUMMARY = 1003;
1202815f58f72f109790585931f601a63ddc02536a5Evan Millar    private static final int CONTACTS_RAW_CONTACTS = 1004;
1212815f58f72f109790585931f601a63ddc02536a5Evan Millar    private static final int CONTACTS_SUMMARY_ID = 1005;
1222815f58f72f109790585931f601a63ddc02536a5Evan Millar    private static final int CONTACTS_SUMMARY_FILTER = 1006;
1232815f58f72f109790585931f601a63ddc02536a5Evan Millar    private static final int CONTACTS_SUMMARY_STREQUENT = 1007;
1242815f58f72f109790585931f601a63ddc02536a5Evan Millar    private static final int CONTACTS_SUMMARY_STREQUENT_FILTER = 1008;
1252815f58f72f109790585931f601a63ddc02536a5Evan Millar    private static final int CONTACTS_SUMMARY_GROUP = 1009;
1264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
13028ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey    private static final int CONTACTS_FILTER_EMAIL = 2005;
1314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
134ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
135ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES_FILTER = 3003;
136ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int POSTALS = 3004;
137a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1396bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
140b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
141b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
142b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE = 7000;
1441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE_ID = 7001;
1451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
14631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
14731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
148ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
150ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
151ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
15235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
15335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
154c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
155c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
156c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
15767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface ContactsQuery {
1585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final String TABLE = Tables.RAW_CONTACTS;
1599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
16067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
1616cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
1626cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
1636cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
164ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
16767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_NAME = 1;
16867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_TYPE = 2;
16967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
17067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataRawContactsQuery {
1725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final String TABLE = Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS;
17367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
17467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
1756cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
1763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
177d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            RawContacts.CONTACT_ID,
1786cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.IS_RESTRICTED,
1793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.MIMETYPE,
180ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
181ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
18367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
184d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
18567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int IS_RESTRICTED = 3;
18667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int MIMETYPE = 4;
18767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
18867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
189d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
190d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS;
19167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
19267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
1936cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
1943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
195d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            ContactsColumns.CONCRETE_ID,
1963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.CONCRETE_ID,
1973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Phone.NUMBER,
1983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Email.DATA,
199d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            ContactsColumns.OPTIMAL_PRIMARY_PHONE_ID,
200d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            ContactsColumns.FALLBACK_PRIMARY_PHONE_ID,
201d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            ContactsColumns.OPTIMAL_PRIMARY_EMAIL_ID,
202d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            ContactsColumns.FALLBACK_PRIMARY_EMAIL_ID,
203ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
204ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
205d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
20667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
207d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
20867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int MIMETYPE_ID = 3;
20967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int PHONE_NUMBER = 4;
21067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int EMAIL_DATA = 5;
21167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int OPTIMAL_PHONE_ID = 6;
21267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int FALLBACK_PHONE_ID = 7;
21367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int OPTIMAL_EMAIL_ID = 8;
21467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int FALLBACK_EMAIL_ID = 9;
21580c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
216ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DisplayNameQuery {
21967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
2243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA2,
2253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            StructuredName.DISPLAY_NAME,
2263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 0;
2293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 1;
2303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA2 = 2;
2313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DISPLAY_NAME = 3;
2323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DataQuery {
23567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2405ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
2423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA1,
2433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA2,
2443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA3,
2453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA4,
2463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA5,
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA6,
2483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA7,
2493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA8,
2503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA9,
2513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.DATA10,
25267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA11,
25367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA12,
25467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA13,
25567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA14,
25667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Data.DATA15,
2573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int ID = 0;
2603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
2633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA1 = 4;
2643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA2 = 5;
2653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA3 = 6;
2663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA4 = 7;
2673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA5 = 8;
2683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA6 = 9;
2693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA7 = 10;
2703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA8 = 11;
2713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA9 = 12;
2723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DATA10 = 13;
27367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA11 = 14;
27467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA12 = 15;
27567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA13 = 16;
27667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA14 = 17;
27767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA15 = 18;
2783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private interface DataIdQuery {
281321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
28220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
28320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
284321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
285321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
28620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
28720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    // Higher number represents higher priority in choosing what data to use for the display name
2893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_EMAIL = 1;
2903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_PHONE = 2;
2913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_ORGANIZATION = 3;
2923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DISPLAY_NAME_PRIORITY_STRUCTURED_NAME = 4;
2933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final HashMap<String, Integer> sDisplayNamePriorities;
2953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    static {
2963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities = new HashMap<String, Integer>();
2973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(StructuredName.CONTENT_ITEM_TYPE,
2983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_STRUCTURED_NAME);
2993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Organization.CONTENT_ITEM_TYPE,
3003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_ORGANIZATION);
3013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Phone.CONTENT_ITEM_TYPE,
3023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_PHONE);
3033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        sDisplayNamePriorities.put(Email.CONTENT_ITEM_TYPE,
3043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                DISPLAY_NAME_PRIORITY_EMAIL);
3053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
30631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /** Contains just the contacts columns */
3084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
309d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Contains the contact columns along with primary phone */
310d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sContactsSummaryProjectionMap;
311d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Contains the data, contacts, and contact columns, for joined tables. */
3120c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar    private static final HashMap<String, String> sDataRawContactsContactProjectionMap;
313d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Contains the data, contacts, group sourceid and contact columns, for joined tables. */
3140c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar    private static final HashMap<String, String> sDataRawContactsGroupsContactProjectionMap;
3152815f58f72f109790585931f601a63ddc02536a5Evan Millar    /** Contains the contacts, and raw contact columns, for joined tables. */
3162815f58f72f109790585931f601a63ddc02536a5Evan Millar    private static final HashMap<String, String> sRawContactsContactsProjectionMap;
317d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Contains just the contacts columns */
318d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
319a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the data columns */
3209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataGroupsProjectionMap;
3219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
3220c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar    private static final HashMap<String, String> sDataRawContactsGroupsProjectionMap;
323a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains the data and contacts columns, for joined tables */
3240c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar    private static final HashMap<String, String> sDataRawContactsProjectionMap;
325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
328ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
329373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
330b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
331373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains Presence columns */
332373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    private static final HashMap<String, String> sPresenceProjectionMap;
3337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
334c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the contact id associated with a data record. */
335d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String sNestedRawContactIdSelect;
336c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the mimetype id associated with a data record. */
337c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedMimetypeSelect;
338d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Sql select statement that returns the contact id associated with a contact record. */
339d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String sNestedContactIdSelect;
340d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Sql select statement that returns a list of contact ids associated with an contact record. */
341c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdListSelect;
342c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
343c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "primary" is selected.*/
344c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetPrimaryWhere;
345c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
346c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "super primary" is selected.*/
347c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetSuperPrimaryWhere;
348b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /** Sql where statement for filtering on groups. */
349d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String sContactsInGroupSelect;
350c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
351c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
353c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
354d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Precompiled sql statement for incrementing times contacted for an contact */
355f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private SQLiteStatement mLastTimeContactedUpdate;
3563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
3573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private SQLiteStatement mContactDisplayNameUpdate;
358a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
3594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
3604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
361a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
362d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
363d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
364d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
3652815f58f72f109790585931f601a63ddc02536a5Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/raw_contacts", CONTACTS_RAW_CONTACTS);
366d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts_summary", CONTACTS_SUMMARY);
367d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts_summary/#", CONTACTS_SUMMARY_ID);
368d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts_summary/filter/*",
369d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                CONTACTS_SUMMARY_FILTER);
370d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts_summary/strequent/",
371d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                CONTACTS_SUMMARY_STREQUENT);
372d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts_summary/strequent/filter/*",
373d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                CONTACTS_SUMMARY_STREQUENT_FILTER);
374d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts_summary/group/*",
375d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                CONTACTS_SUMMARY_GROUP);
376d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
37731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3785ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
3795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
3805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
3815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/filter_email/*",
382b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                CONTACTS_FILTER_EMAIL);
383b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
3854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
386ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
387ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
388ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
3891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
390ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
391ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
392ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
393ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
39435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
39535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
396a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
397b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
398b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
399b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
400b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
402bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
403bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
4041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
405c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
406c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
407c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
408c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
409c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
410c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
411c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
412fec4e13316f2731d84394e5fa2f93af3febdc20cEvan Millar        HashMap<String, String> columns;
4134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
414d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        // Contacts projection map
4156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns = new HashMap<String, String>();
416d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts._ID, "contacts._id AS _id");
417d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.DISPLAY_NAME, ContactsColumns.CONCRETE_DISPLAY_NAME + " AS "
418d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Contacts.DISPLAY_NAME);
419d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.LAST_TIME_CONTACTED, ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
420d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + " AS " + Contacts.LAST_TIME_CONTACTED);
421d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.TIMES_CONTACTED, ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS "
422d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Contacts.TIMES_CONTACTED);
423d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.STARRED, ContactsColumns.CONCRETE_STARRED + " AS "
424d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Contacts.STARRED);
425d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
426d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
427d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.PRIMARY_PHONE_ID, Contacts.PRIMARY_PHONE_ID);
428d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.PRIMARY_EMAIL_ID, Contacts.PRIMARY_EMAIL_ID);
429d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.CUSTOM_RINGTONE, ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS "
430d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Contacts.CUSTOM_RINGTONE);
431d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.SEND_TO_VOICEMAIL, ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
432d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + " AS " + Contacts.SEND_TO_VOICEMAIL);
433d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(ContactsColumns.FALLBACK_PRIMARY_PHONE_ID,
434d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                ContactsColumns.FALLBACK_PRIMARY_PHONE_ID);
435d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(ContactsColumns.FALLBACK_PRIMARY_EMAIL_ID,
436d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                ContactsColumns.FALLBACK_PRIMARY_EMAIL_ID);
437d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sContactsProjectionMap = columns;
4386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
4391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns = new HashMap<String, String>();
440d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.putAll(sContactsProjectionMap);
441c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov
442d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        // Contacts primaries projection map. The overall presence status is
443c62855331805c2744a097ef6ea625652197bfb87Dmitri Plotnikov        // the most-present value, as indicated by the largest value.
44400ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        columns.put(Contacts.PRESENCE_STATUS, "MAX(" + Presence.PRESENCE_STATUS + ") AS "
44500ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                + Contacts.PRESENCE_STATUS);
446d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.PRIMARY_PHONE_TYPE, CommonDataKinds.Phone.TYPE);
447d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.PRIMARY_PHONE_LABEL, CommonDataKinds.Phone.LABEL);
448d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Contacts.PRIMARY_PHONE_NUMBER, CommonDataKinds.Phone.NUMBER);
449d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sContactsSummaryProjectionMap = columns;
45000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4516cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts projection map
4524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
4535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        columns.put(RawContacts._ID, Tables.RAW_CONTACTS + "." + RawContacts._ID + " AS _id");
454d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
4556cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        columns.put(RawContacts.ACCOUNT_NAME,
4565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                OpenHelper.RawContactsColumns.CONCRETE_ACCOUNT_NAME
4575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        + " AS " + RawContacts.ACCOUNT_NAME);
4586cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        columns.put(RawContacts.ACCOUNT_TYPE,
4595ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                OpenHelper.RawContactsColumns.CONCRETE_ACCOUNT_TYPE
4605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        + " AS " + RawContacts.ACCOUNT_TYPE);
4616cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        columns.put(RawContacts.SOURCE_ID,
4625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                OpenHelper.RawContactsColumns.CONCRETE_SOURCE_ID
4635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        + " AS " + RawContacts.SOURCE_ID);
4646cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        columns.put(RawContacts.VERSION,
4655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                OpenHelper.RawContactsColumns.CONCRETE_VERSION
4665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        + " AS " + RawContacts.VERSION);
4676cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        columns.put(RawContacts.DIRTY,
4685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                OpenHelper.RawContactsColumns.CONCRETE_DIRTY
4695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        + " AS " + RawContacts.DIRTY);
47033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        columns.put(RawContacts.DELETED,
4715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                OpenHelper.RawContactsColumns.CONCRETE_DELETED
4725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                        + " AS " + RawContacts.DELETED);
4733cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.TIMES_CONTACTED,
4743cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
4753cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                        + " AS " + People.TIMES_CONTACTED);
4763cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.LAST_TIME_CONTACTED,
4773cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
4783cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                        + " AS " + People.LAST_TIME_CONTACTED);
4793cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.CUSTOM_RINGTONE,
4803cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
4813cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                        + " AS " + People.CUSTOM_RINGTONE);
4823cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.SEND_TO_VOICEMAIL,
4833cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
4843cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                        + " AS " + People.SEND_TO_VOICEMAIL);
4853cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.STARRED,
4863cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.STARRED
4873cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                        + " AS " + People.STARRED);
4883cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
4893cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.SYNC1,
4903cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.SYNC1 + " AS " + RawContacts.SYNC1);
4913cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.SYNC2,
4923cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.SYNC2 + " AS " + RawContacts.SYNC2);
4933cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.SYNC3,
4943cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.SYNC3 + " AS " + RawContacts.SYNC3);
4953cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(RawContacts.SYNC4,
4963cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Tables.RAW_CONTACTS + "." + RawContacts.SYNC4 + " AS " + RawContacts.SYNC4);
497d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sRawContactsProjectionMap = columns;
4984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4992815f58f72f109790585931f601a63ddc02536a5Evan Millar        columns = new HashMap<String, String>();
5002815f58f72f109790585931f601a63ddc02536a5Evan Millar        columns.putAll(sContactsProjectionMap);
5012815f58f72f109790585931f601a63ddc02536a5Evan Millar        columns.putAll(sRawContactsProjectionMap);
5022815f58f72f109790585931f601a63ddc02536a5Evan Millar        sRawContactsContactsProjectionMap = columns;
5032815f58f72f109790585931f601a63ddc02536a5Evan Millar
5044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Data projection map
5054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
5065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        columns.put(Data._ID, Tables.DATA + "." + Data._ID + " AS _id");
5075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        columns.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
50867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.RES_PACKAGE, PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE);
509508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        columns.put(Data.MIMETYPE, Data.MIMETYPE);
510c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
511c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
512f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
5137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA1, "data.data1 as data1");
5147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA2, "data.data2 as data2");
5157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA3, "data.data3 as data3");
5167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA4, "data.data4 as data4");
5177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA5, "data.data5 as data5");
5187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA6, "data.data6 as data6");
5197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA7, "data.data7 as data7");
5207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA8, "data.data8 as data8");
5217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA9, "data.data9 as data9");
5227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA10, "data.data10 as data10");
52367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA11, "data.data11 as data11");
52467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA12, "data.data12 as data12");
52567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA13, "data.data13 as data13");
52667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA14, "data.data14 as data14");
52767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Data.DATA15, "data.data15 as data15");
5283cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Data.SYNC1, Tables.DATA + "." + Data.SYNC1 + " AS " + Data.SYNC1);
5293cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Data.SYNC2, Tables.DATA + "." + Data.SYNC2 + " AS " + Data.SYNC2);
5303cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Data.SYNC3, Tables.DATA + "." + Data.SYNC3 + " AS " + Data.SYNC3);
5313cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Data.SYNC4, Tables.DATA + "." + Data.SYNC4 + " AS " + Data.SYNC4);
53267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupsColumns.CONCRETE_SOURCE_ID + " AS "
53367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                + GroupMembership.GROUP_SOURCE_ID);
53420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
53520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // TODO: remove this projection
536d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        // Mappings used for backwards compatibility.
537d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put("number", Phone.NUMBER);
5389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataGroupsProjectionMap = columns;
539a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data, groups and contacts projection map for joins. _id comes from the data table
541a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns = new HashMap<String, String>();
542d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.putAll(sRawContactsProjectionMap);
5439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
544d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Data.RAW_CONTACT_ID, DataColumns.CONCRETE_RAW_CONTACT_ID);
5450c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar        sDataRawContactsGroupsProjectionMap = columns;
5469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
5479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
5489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
5490c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar        columns.putAll(sDataRawContactsGroupsProjectionMap);
5509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
5510c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar        sDataRawContactsProjectionMap = columns;
5527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
553de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        // Data and contacts projection map for joins. _id comes from the data table
554de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns = new HashMap<String, String>();
555d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.putAll(sContactsProjectionMap);
556d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.putAll(sRawContactsProjectionMap); //
5579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
558d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Data.RAW_CONTACT_ID, DataColumns.CONCRETE_RAW_CONTACT_ID);
5590c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar        sDataRawContactsGroupsContactProjectionMap = columns;
5609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
5619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
5629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
5630c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar        columns.putAll(sDataRawContactsGroupsContactProjectionMap);
5649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
5650c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar        sDataRawContactsContactProjectionMap = columns;
566c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
567ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
568ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
569ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups._ID, "groups._id AS _id");
570035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
571035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
5729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
5739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
5749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
57567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.RES_PACKAGE, PackagesColumns.PACKAGE + " AS " + Groups.RES_PACKAGE);
576ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
57767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
578ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
5793cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
5803cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
5813cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYNC1, Tables.GROUPS + "." + Groups.SYNC1 + " AS " + Groups.SYNC1);
5823cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYNC2, Tables.GROUPS + "." + Groups.SYNC2 + " AS " + Groups.SYNC2);
5833cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYNC3, Tables.GROUPS + "." + Groups.SYNC3 + " AS " + Groups.SYNC3);
5843cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYNC4, Tables.GROUPS + "." + Groups.SYNC4 + " AS " + Groups.SYNC4);
585ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
586ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5876cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
588ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
589ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
590ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
591d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
592d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
593ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
594ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
595ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
596ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
597d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
598d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
599ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
600ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + " AND " + Clauses.HAS_PRIMARY_PHONE + ") AS " + Groups.SUMMARY_WITH_PHONES);
601ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
602ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
603ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
604b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
605b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
606b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
607b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
608d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(AggregationExceptions.CONTACT_ID,
609d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                "raw_contacts1." + RawContacts.CONTACT_ID
610d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + " AS " + AggregationExceptions.CONTACT_ID);
6115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID, AggregationExceptionColumns.RAW_CONTACT_ID2);
612b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
613b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
614373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov
615373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
616373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence._ID, Presence._ID);
617373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.RAW_CONTACT_ID, Presence.RAW_CONTACT_ID);
618373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.DATA_ID, Presence.DATA_ID);
619373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.IM_ACCOUNT, Presence.IM_ACCOUNT);
620373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.IM_HANDLE, Presence.IM_HANDLE);
621373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.IM_PROTOCOL, Presence.IM_PROTOCOL);
622373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.PRESENCE_STATUS, Presence.PRESENCE_STATUS);
623373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.PRESENCE_CUSTOM_STATUS, Presence.PRESENCE_CUSTOM_STATUS);
624373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        sPresenceProjectionMap = columns;
625373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov
626d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sNestedRawContactIdSelect = "SELECT " + Data.RAW_CONTACT_ID + " FROM " + Tables.DATA + " WHERE "
627c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + Data._ID + "=?";
628c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedMimetypeSelect = "SELECT " + DataColumns.MIMETYPE_ID + " FROM " + Tables.DATA
629c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Data._ID + "=?";
630d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sNestedContactIdSelect = "SELECT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS
631d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + " WHERE " + RawContacts._ID + "=(" + sNestedRawContactIdSelect + ")";
6325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        sNestedContactIdListSelect = "SELECT " + RawContacts._ID + " FROM " + Tables.RAW_CONTACTS
633d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + " WHERE " + RawContacts.CONTACT_ID + "=(" + sNestedContactIdSelect + ")";
634d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sSetPrimaryWhere = Data.RAW_CONTACT_ID + "=(" + sNestedRawContactIdSelect + ") AND "
635c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
6365ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        sSetSuperPrimaryWhere = Data.RAW_CONTACT_ID + " IN (" + sNestedContactIdListSelect + ") AND "
637c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
638d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sContactsInGroupSelect = ContactsColumns.CONCRETE_ID + " IN (SELECT "
639d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS + " WHERE ("
6406cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                + RawContactsColumns.CONCRETE_ID + " IN (SELECT " + Tables.DATA + "."
6415ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                + Data.RAW_CONTACT_ID + " FROM " + Tables.DATA_JOIN_MIMETYPES + " WHERE ("
642b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + "' AND "
643b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + GroupMembership.GROUP_ROW_ID + "=(SELECT " + Tables.GROUPS + "."
644b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Groups._ID + " FROM " + Tables.GROUPS + " WHERE " + Groups.TITLE + "=?)))))";
6454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
6464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
6473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
6483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
6493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
6503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
6513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
6533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
6553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
6563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
6593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
6603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
6615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
662e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
663e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
664e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
665e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
666e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                setIsPrimary(dataId);
667e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
668e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
6695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            fixContactDisplayName(db, rawContactId);
670e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
6713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
6743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
6753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
6763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
6773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
6783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            throw new UnsupportedOperationException();
6793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
6823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            long dataId = c.getLong(DataQuery.ID);
6835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long rawContactId = c.getLong(DataQuery.RAW_CONTACT_ID);
6843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            boolean primary = c.getInt(DataQuery.IS_PRIMARY) != 0;
6853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
6863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
6875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
6885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixContactDisplayName(db, rawContactId);
6893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
6903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
6913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
6935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
6945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, rawContactId);
6953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
6963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                ContactsProvider2.this.setIsPrimary(newPrimaryId);
6973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
6983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
6993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long rawContactId) {
701e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
702e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
7035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Cursor c = queryData(db, rawContactId);
7043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
705e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
706e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    long dataId = c.getLong(DataQuery.ID);
707e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    int type = c.getInt(DataQuery.DATA2);
708e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
709e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
710e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
711e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
7123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
7133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
7143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
7153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
716e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
717e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
718e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
719e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
720e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
721e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
722e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
723e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
724e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
7253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long rawContactId) {
7283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO Lookup integer mimetype IDs' instead of joining for speed
7295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return db.query(DataQuery.TABLE, DataQuery.COLUMNS, Data.RAW_CONTACT_ID + "="
7305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + rawContactId + " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
7313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
7323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7345ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected void fixContactDisplayName(SQLiteDatabase db, long rawContactId) {
735e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (!sDisplayNamePriorities.containsKey(mMimetype)) {
736e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                return;
737e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
738e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
7393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String bestDisplayName = null;
74067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Cursor c = db.query(DisplayNameQuery.TABLE, DisplayNameQuery.COLUMNS,
7415ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId, null, null, null, null);
7423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
7433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                int maxPriority = -1;
7443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                while (c.moveToNext()) {
7453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String mimeType = c.getString(DisplayNameQuery.MIMETYPE);
7463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    boolean primary;
7473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String name;
7483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
7503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        name = c.getString(DisplayNameQuery.DISPLAY_NAME);
7513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = true;
7523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    } else {
7533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        name = c.getString(DisplayNameQuery.DATA2);
7543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
7553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
7563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    if (primary && name != null) {
7583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        Integer priority = sDisplayNamePriorities.get(mimeType);
7593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        if (priority != null && priority > maxPriority) {
7603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            maxPriority = priority;
7613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            bestDisplayName = name;
7623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        }
7633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
7643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
7653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
7673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
7683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            ContactsProvider2.this.setDisplayName(rawContactId, bestDisplayName);
7713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
7733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
7753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
7773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
7783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
7803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
7823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final NameSplitter mNameSplitter;
7843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public StructuredNameRowHandler(NameSplitter nameSplitter) {
7863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
7873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mNameSplitter = nameSplitter;
7883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
7923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            fixStructuredNameComponents(values);
7935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return super.insert(db, rawContactId, values);
7943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
7973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
7983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO Parse the full name if it has changed and replace pre-existing piece parts.
7993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Parses the supplied display name, but only if the incoming values do not already contain
8033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * structured name parts.  Also, if the display name is not provided, generate one by
8043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * concatenating first name and last name
8053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         *
8063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * TODO see if the order of first and last names needs to be conditionally reversed for
8073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * some locales, e.g. China.
8083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
8093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private void fixStructuredNameComponents(ContentValues values) {
8103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String fullName = values.getAsString(StructuredName.DISPLAY_NAME);
8113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (!TextUtils.isEmpty(fullName)
8123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.PREFIX))
8133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.GIVEN_NAME))
8143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.MIDDLE_NAME))
8153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.FAMILY_NAME))
8163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    && TextUtils.isEmpty(values.getAsString(StructuredName.SUFFIX))) {
8173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
8183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                mNameSplitter.split(name, fullName);
8193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.PREFIX, name.getPrefix());
8213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.GIVEN_NAME, name.getGivenNames());
8223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.MIDDLE_NAME, name.getMiddleName());
8233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.FAMILY_NAME, name.getFamilyName());
8243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                values.put(StructuredName.SUFFIX, name.getSuffix());
8253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (TextUtils.isEmpty(fullName)) {
8283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                String givenName = values.getAsString(StructuredName.GIVEN_NAME);
8293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                String familyName = values.getAsString(StructuredName.FAMILY_NAME);
8303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                if (TextUtils.isEmpty(givenName)) {
8313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = familyName;
8323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                } else if (TextUtils.isEmpty(familyName)) {
8333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = givenName;
8343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                } else {
8353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    fullName = givenName + " " + familyName;
8363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
8373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                if (!TextUtils.isEmpty(fullName)) {
8393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    values.put(StructuredName.DISPLAY_NAME, fullName);
8403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
8413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
8443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
8463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
8483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
8493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
8513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
8523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
8533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
8543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
8583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int type;
8593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String label;
8603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (values.containsKey(mTypeColumn)) {
8613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                type = values.getAsInteger(mTypeColumn);
8623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } else {
8633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                type = BaseTypes.TYPE_CUSTOM;
8643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (values.containsKey(mLabelColumn)) {
8663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                label = values.getAsString(mLabelColumn);
8673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } else {
8683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                label = null;
8693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (type != BaseTypes.TYPE_CUSTOM && label != null) {
8727a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException(mLabelColumn + " value can only be specified with "
8733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        + mTypeColumn + "=" + BaseTypes.TYPE_CUSTOM + "(custom)");
8743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (type == BaseTypes.TYPE_CUSTOM && label == null) {
8777a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException(mLabelColumn + " value must be specified when "
8783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        + mTypeColumn + "=" + BaseTypes.TYPE_CUSTOM + "(custom)");
8793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return super.insert(db, rawContactId, values);
8823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor cursor) {
8863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            // TODO read the data and check the constraint
8873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
8893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
8913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
8933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
8943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
8975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
8985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long id = super.insert(db, rawContactId, values);
8995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            fixContactDisplayName(db, rawContactId);
9003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return id;
9013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
9043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
9053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
9063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
9073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
9083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
9093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
9103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
9133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
914e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
915e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
916e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
917e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
918e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
919e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
920e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
9215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
9225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long id = super.insert(db, rawContactId, values);
9235ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            fixContactDisplayName(db, rawContactId);
924e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return id;
925e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
926e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
927e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
928e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
929e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
930e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
931e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
932e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
933e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
934e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
935e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
936e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
937e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
938e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
9393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
9403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
9423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
9433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
9465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
947e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            ContentValues phoneValues = new ContentValues();
948e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String number = values.getAsString(Phone.NUMBER);
949e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
950e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
951e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
952e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
953e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
954e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
9555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long id = super.insert(db, rawContactId, values);
9563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
957e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
9585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
959e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, id);
960e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
961e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
962e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
9633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return id;
9653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
9683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
9693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
9703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
9713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
9723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
9733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
9743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
9753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
9763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
9773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
9783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
9793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
9823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
98453056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
9854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
98631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
987a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    private ContactAggregator mContactAggregator;
9884097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
989f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
990a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
991a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
99220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
99320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
994a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
99553056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
996a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
997a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
998a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
999a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
1000a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
100153056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
100253056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
1003a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
10044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
10054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
10064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1007b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        final Context context = getContext();
100835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
100931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        mOpenHelper = getOpenHelper(context);
1010a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1011a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mOpenHelper, this, mGlobalSearchSupport);
101253056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mContactAggregator = new ContactAggregator(context, mOpenHelper, mAggregationScheduler);
1013a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1014d51a83ac4f8032b62d9a23b90a8f43d6b7eb2dbbDmitri Plotnikov        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1015c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1016c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_PRIMARY
1017c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetPrimaryWhere);
1018c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1019c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_SUPER_PRIMARY
1020c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetSuperPrimaryWhere);
10215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
10226cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                + RawContacts.TIMES_CONTACTED + "=" + RawContacts.TIMES_CONTACTED + "+1,"
1023d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + RawContacts.LAST_TIME_CONTACTED + "=? WHERE " + RawContacts.CONTACT_ID + "=?");
1024a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
10255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mContactDisplayNameUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
10266cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                + RawContactsColumns.DISPLAY_NAME + "=? WHERE " + RawContacts._ID + "=?");
10273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
102828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
102928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
103028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
103128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
103228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_conjunctions));
10334097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
10343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
10353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1036e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
10373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
10383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
103967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
104067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
10413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
10423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
104367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
104467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL));
10453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
10463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
10473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
10493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            if (!importLegacyContacts()) {
10503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                return false;
10513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            }
10523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
10531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
10544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
10554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
105631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
105731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
105831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
105931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
106031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
10613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
10623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
10633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
10643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
10653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
10663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
10673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (importLegacyContacts(getLegacyContactImporter())) {
10683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
10693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
10703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
10713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
10723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
10733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
10743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
10753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
10763d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
10773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
10783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
10793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
10803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
10813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
10823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
10833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
10843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
10853d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
10863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
10873d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            mContactAggregator.setEnabled(true);
10883d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            mContactAggregator.run();
10893d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
10903d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
10913d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
10923d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
10933d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
10943d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
10953d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1096a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
1097a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
1098a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
1099a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
1100a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
1101a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1102a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
1103a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1104a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1105a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1106a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1107a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1108a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1109a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
1110a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1111a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1112a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1113a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Called when a change has been made.
1114a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1115a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param uri the uri that the change was made to
1116a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1117a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private void onChange(Uri uri) {
1118a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null);
1119a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1120a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
11214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
11224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean isTemporary() {
11234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return false;
11244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
11263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
11273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
11283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
11293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
11303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
11313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
11333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
11364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Uri insert(Uri uri, ContentValues values) {
1137a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1138a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
113935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1140a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
114135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
114235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                id = mOpenHelper.getSyncState().insert(mOpenHelper.getWritableDatabase(), values);
114335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
114435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1145d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1146d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
11476bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
11486bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
11496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
11505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
1151f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
1152d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                id = insertRawContact(values, account);
1153a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1154a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1155a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
11565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
11575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1158035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
1159a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1160a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1161a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1162a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1163035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
1164a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1165a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1166a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1167ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1168ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
1169ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                id = insertGroup(values, account);
1170ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1171ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1172ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
11731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
11741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
11751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
11761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
11771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1178a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
1179f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1180a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1181a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
11827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
11837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
11847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
11857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
11867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final Uri result = ContentUris.withAppendedId(uri, id);
1187a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        onChange(result);
1188a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return result;
1189a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1190a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1191a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1192035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
1193035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
1194035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
1195035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
1196035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
11977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1198035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
1199035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
12006cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
12016cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1202035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
1203035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
1204035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
1205035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
1206035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
1207035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
1208035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1209035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
12106cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, account.mName);
12116cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, account.mType);
1212035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1213035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
12147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
12157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
12167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1217d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
12186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
12196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
12206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
12216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
1222d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
1223a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregates are created automatically");
12246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
12256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
12266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
1227a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
1228a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1229a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1230f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
1231a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1232a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1233d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertRawContact(ContentValues values, Account account) {
12346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        /*
12356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * The contact record is inserted in the contacts table, but it needs to
12366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * be processed by the aggregator before it will be returned by the
12376bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * "aggregates" queries.
12386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         */
1239a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
12406bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1241a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
1242d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        overriddenValues.putNull(RawContacts.CONTACT_ID);
1243f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
12447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
12457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
12467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
12473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
12483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
12493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            overriddenValues.put(RawContacts.AGGREGATION_MODE,
12503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                    RawContacts.AGGREGATION_MODE_DISABLED);
12513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
12523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1253c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return db.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, overriddenValues);
1254a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1255a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1256a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1257a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
1258a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1259a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1260a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1261a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1262035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private long insertData(ContentValues values) {
12636cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DISABLED;
1264a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1265a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1266a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
1267a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        db.beginTransaction();
1268a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        try {
126920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.clear();
127020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.putAll(values);
127120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
12725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
1273a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
127467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // Replace package with internal mapping
127520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            final String packageName = mValues.getAsString(Data.RES_PACKAGE);
127667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            if (packageName != null) {
127720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
127867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            }
127920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
128067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
1281619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // Replace mimetype with internal mapping
128220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            final String mimeType = mValues.getAsString(Data.MIMETYPE);
128320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (TextUtils.isEmpty(mimeType)) {
12847a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException(Data.MIMETYPE + " is required");
128520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
128620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
128720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
128820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.MIMETYPE);
1289508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
1290321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            resolveGroupSourceIdInValues(rawContactId, mimeType, db, mValues, true /* isInsert */);
12914097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
12925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            id = getDataRowHandler(mimeType).insert(db, rawContactId, mValues);
1293a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
12945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            aggregationMode = mContactAggregator.markContactForAggregation(rawContactId);
1295a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1296a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.setTransactionSuccessful();
1297a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        } finally {
1298a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.endTransaction();
1299a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1300a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1301f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        triggerAggregation(id, aggregationMode);
1302a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
13034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
13044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
13055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private void triggerAggregation(long rawContactId, int aggregationMode) {
1306f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
13076cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT:
1308f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.schedule();
1309f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1310f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
13116cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_IMMEDITATE:
13125ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                mContactAggregator.aggregateContact(rawContactId);
1313f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1314f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
13156cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
1316f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                // Do nothing
1317f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
1318f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
1319f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
1320f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
1321a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
13225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
13239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
13249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
13255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
13269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
13279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
13289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
13299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
13309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
13315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId) {
13329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
13336cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, RawContacts._ID + "="
13345ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                + rawContactId, null, null, null, null);
13359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
13369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
133767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountName = c.getString(ContactsQuery.ACCOUNT_NAME);
133867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountType = c.getString(ContactsQuery.ACCOUNT_TYPE);
13399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
13409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
13419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
13429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
13439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
13449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
13459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
13469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
13479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
13489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
13499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
13509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
13519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
13529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
13535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
13546cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
13559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
13569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                new String[]{sourceId, account.mName, account.mType}, null, null, null);
13579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
13589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
13599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
13609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
13619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
13629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.mName);
13639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.mType);
13649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
13659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
13669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
13679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
13689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
13699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
13709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
13719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
13729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
13739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
13749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
13759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
13769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
13779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
137820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
137920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
138020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private int deleteData(String selection, String[] selectionArgs) {
138120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
138220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
138320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        db.beginTransaction();
138420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
138520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
138620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Note that the query will return data according to the access restrictions,
138720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // so we don't need to worry about deleting data we don't have permission to read.
138820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            Cursor c = query(Data.CONTENT_URI, DataIdQuery.COLUMNS, selection, selectionArgs, null);
138920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            try {
139020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                while(c.moveToNext()) {
139120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    long dataId = c.getLong(DataIdQuery._ID);
139220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    count += deleteData(dataId);
139320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
139420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            } finally {
139520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                c.close();
139620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
139720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.setTransactionSuccessful();
139820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
139920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.endTransaction();
140020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
140120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
140220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
140320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
140420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
140520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
140620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
140720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Cursor c = db.query(DataQuery.TABLE, DataQuery.COLUMNS,
140820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
140920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // TODO apply restrictions
141020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
141120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
141220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
141320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
141420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
141520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            String mimeType = c.getString(DataQuery.MIMETYPE);
141620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
141720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
141820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
141920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
142020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
142120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
142220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
142320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
142420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
14257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
142620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
142720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
142820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
142920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            return getDataRowHandler(mimeType).delete(db, c);
143020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
143120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
143220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
143320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
143420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
143520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
1436d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Delete the given {@link Data} row, fixing up any {@link Contacts}
1437ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * primaries that reference it.
1438ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1439ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private int deleteData(long dataId) {
1440ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1441ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1442ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimePhone = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
1443ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimeEmail = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1444ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1445ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Check to see if the data about to be deleted was a super-primary on
1446ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // the parent aggregate, and set flags to fix-up once deleted.
1447ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long aggId = -1;
1448ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long mimeId = -1;
1449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        String dataRaw = null;
1450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixOptimal = false;
1451ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixFallback = false;
1452ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1453ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor cursor = null;
1454ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        try {
1455d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            cursor = db.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
145667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
1457ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor.moveToFirst()) {
1458d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                aggId = cursor.getLong(DataContactsQuery.CONTACT_ID);
1459d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                mimeId = cursor.getLong(DataContactsQuery.MIMETYPE_ID);
1460ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
1461d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    dataRaw = cursor.getString(DataContactsQuery.PHONE_NUMBER);
1462d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    fixOptimal = (cursor.getLong(DataContactsQuery.OPTIMAL_PHONE_ID) == dataId);
1463d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    fixFallback = (cursor.getLong(DataContactsQuery.FALLBACK_PHONE_ID) == dataId);
1464ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
1465d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    dataRaw = cursor.getString(DataContactsQuery.EMAIL_DATA);
1466d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    fixOptimal = (cursor.getLong(DataContactsQuery.OPTIMAL_EMAIL_ID) == dataId);
1467d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    fixFallback = (cursor.getLong(DataContactsQuery.FALLBACK_EMAIL_ID) == dataId);
1468ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1469ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1470ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        } finally {
1471ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor != null) {
1472ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.close();
1473ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor = null;
1474ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1476ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1477ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Delete the requested data item.
1478ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        int dataDeleted = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
1479ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1480ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Fix-up any super-primary values that are now invalid.
1481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (fixOptimal || fixFallback) {
1482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final ContentValues values = new ContentValues();
1483ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final StringBuilder scoreClause = new StringBuilder();
1484ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1485ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String SCORE = "score";
1486ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1487ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Build scoring clause that will first pick data items under the
1488ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // same aggregate that have identical values, otherwise fall back to
1489ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // normal primary scoring from the member contacts.
1490ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("(CASE WHEN ");
1491ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (mimeId == mimePhone) {
1492ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Phone.NUMBER);
1493ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            } else if (mimeId == mimeEmail) {
1494ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Email.DATA);
1495ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1496ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("=");
1497ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            DatabaseUtils.appendEscapedSQLString(scoreClause, dataRaw);
1498ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append(" THEN 2 ELSE " + Data.IS_PRIMARY + " END) AS " + SCORE);
1499ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1500ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String[] PROJ_PRIMARY = new String[] {
1501ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    DataColumns.CONCRETE_ID,
15026cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                    RawContacts.IS_RESTRICTED,
1503ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    scoreClause.toString(),
1504ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            };
1505ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1506ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_DATA_ID = 0;
1507ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_IS_RESTRICTED = 1;
150867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            final int COL_SCORE = 2;
1509ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1510d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS, PROJ_PRIMARY,
1511d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    ContactsColumns.CONCRETE_ID + "=" + aggId + " AND " + DataColumns.MIMETYPE_ID
1512ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            + "=" + mimeId, null, null, null, SCORE);
1513ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1514ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixOptimal) {
1515ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
151667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                String colIsRestricted = null;
1517ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
1518d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    colId = ContactsColumns.OPTIMAL_PRIMARY_PHONE_ID;
1519d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    colIsRestricted = ContactsColumns.OPTIMAL_PRIMARY_PHONE_IS_RESTRICTED;
1520ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
1521d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    colId = ContactsColumns.OPTIMAL_PRIMARY_EMAIL_ID;
1522d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    colIsRestricted = ContactsColumns.OPTIMAL_PRIMARY_EMAIL_IS_RESTRICTED;
1523ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1524ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1525ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixOptimal told us that
1526ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
1527ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
152867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                values.putNull(colIsRestricted);
1529ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1530ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // When finding a new optimal primary, we only care about the
1531ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // highest scoring value, regardless of source.
1532ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (cursor.moveToFirst()) {
1533ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimal = cursor.getLong(COL_DATA_ID);
153467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    final long newIsRestricted = cursor.getLong(COL_IS_RESTRICTED);
1535ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1536ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimal != 0) {
1537ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, newOptimal);
1538ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
153967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    if (newIsRestricted != 0) {
154067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                        values.put(colIsRestricted, newIsRestricted);
1541ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
1542ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1543ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1544ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1545ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixFallback) {
1546ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
1547ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
1548d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    colId = ContactsColumns.FALLBACK_PRIMARY_PHONE_ID;
1549ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
1550d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    colId = ContactsColumns.FALLBACK_PRIMARY_EMAIL_ID;
1551ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1552ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1553ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixFallback told us that
1554ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
1555ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
1556ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1557ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // The best fallback value is the highest scoring data item that
1558ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // hasn't been restricted.
1559ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.moveToPosition(-1);
1560ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                while (cursor.moveToNext()) {
1561ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final boolean isRestricted = (cursor.getInt(COL_IS_RESTRICTED) == 1);
1562ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (!isRestricted) {
1563ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, cursor.getLong(COL_DATA_ID));
1564ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        break;
1565ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
1566ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1567ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1568ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1569d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            // Push through any contact updates we have
1570ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (values.size() > 0) {
1571d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                db.update(Tables.CONTACTS, values, ContactsColumns.CONCRETE_ID + "=" + aggId,
1572ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        null);
1573ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1574ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1575ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1576ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return dataDeleted;
1577ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1578ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1579ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
1580ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
1581ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
1582ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private long insertGroup(ContentValues values, Account account) {
1583ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1584ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1585ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
1586ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
1587ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
1588ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
1589ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1590ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
159167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE);
159267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
159367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            overriddenValues.put(GroupsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
159467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
159567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        overriddenValues.remove(Groups.RES_PACKAGE);
1596ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1597ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return db.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
1598ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1599ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1600ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
16011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
16021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
160370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public long insertPresence(ContentValues values) {
16041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
16051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
16061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String protocol = values.getAsString(Presence.IM_PROTOCOL);
16071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(handle) || TextUtils.isEmpty(protocol)) {
16081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            throw new IllegalArgumentException("IM_PROTOCOL and IM_HANDLE are required");
16091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
16101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
16111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
1612a01e50cb1a5dd21293f8a8fe43f3fe0bf6349164Jeff Sharkey        boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == Integer.parseInt(protocol);
16131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
161470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        StringBuilder selection = new StringBuilder();
16151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
16161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
161770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append("(" + Clauses.WHERE_IM_MATCHES + ") OR ("
161870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    + Clauses.WHERE_EMAIL_MATCHES + ")");
16191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle, handle };
16201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
162170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append(Clauses.WHERE_IM_MATCHES);
16221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle };
16231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
16241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
162570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (values.containsKey(Presence.DATA_ID)) {
162670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append(" AND " + DataColumns.CONCRETE_ID + "=")
162770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    .append(values.getAsLong(Presence.DATA_ID));
162870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
162970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
16305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        if (values.containsKey(Presence.RAW_CONTACT_ID)) {
1631d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            selection.append(" AND " + DataColumns.CONCRETE_RAW_CONTACT_ID + "=")
16325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    .append(values.getAsLong(Presence.RAW_CONTACT_ID));
163370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
163470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
163500ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        selection.append(" AND ").append(getContactsRestrictions());
163670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
16371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
16385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        long rawContactId = -1;
163970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
16401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
16411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
164270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            cursor = db.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
164370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    selection.toString(), selectionArgs, null, null, null);
16441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
164567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
16465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
16471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
16481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
16491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
16501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
16511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
165231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
165331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
165431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
16551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
16561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
16571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.DATA_ID, dataId);
16585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        values.put(Presence.RAW_CONTACT_ID, rawContactId);
16591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
16601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Insert the presence update
16611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long presenceId = db.replace(Tables.PRESENCE, null, values);
16621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return presenceId;
16631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
16641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
16654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
16664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int delete(Uri uri, String selection, String[] selectionArgs) {
1667508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1668508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
1669508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
167035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
167135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().delete(db, selection, selectionArgs);
167235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1673d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
1674d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
16756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1676d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                // Remove references to the contact first
16776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                ContentValues values = new ContentValues();
1678d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values.putNull(RawContacts.CONTACT_ID);
16795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                db.update(Tables.RAW_CONTACTS, values,
1680d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        RawContacts.CONTACT_ID + "=" + contactId, null);
16816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1682d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                return db.delete(Tables.CONTACTS, BaseColumns._ID + "=" + contactId, null);
16836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
16846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
16855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
168633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                return deleteRawContact(uri);
1687508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1688508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
168920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
169020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return deleteData(selection, selectionArgs);
169120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
169220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1693508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID: {
1694508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
1695ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return deleteData(dataId);
1696ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1697ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1698ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1699ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1700ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final long groupMembershipMimetypeId = mOpenHelper
1701ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1702ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int groupsDeleted = db.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
1703ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
1704ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
1705ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupId, null);
1706ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1707ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return groupsDeleted + dataDeleted;
1708508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1709508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
17101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
17111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return db.delete(Tables.PRESENCE, null, null);
17121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
17131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1714508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            default:
17153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
1716508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
17174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
17184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
171933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    private int deleteRawContact(Uri uri) {
17207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        final String flag = uri.getQueryParameter(RawContacts.DELETE_PERMANENTLY);
17217a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        final boolean permanently = flag != null && "true".equals(flag.toLowerCase());
17227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        final long rawContactId = ContentUris.parseId(uri);
17237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        return deleteRawContact(rawContactId, permanently);
172433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
172533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
17265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public int deleteRawContact(long rawContactId, boolean permanently) {
172733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
172833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
1729c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // TODO delete aggregation exceptions
1730c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        mOpenHelper.removeContactIfSingleton(rawContactId);
173133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        if (permanently) {
1732373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov            db.delete(Tables.PRESENCE, Presence.RAW_CONTACT_ID + "=" + rawContactId, null);
17335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return db.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
173433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
173533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            mValues.clear();
173633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            mValues.put(RawContacts.DELETED, true);
1737c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
1738c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            mValues.putNull(RawContacts.CONTACT_ID);
17395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return updateRawContact(rawContactId, mValues, null, null);
174033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
174133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
174233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
1743f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private static Account readAccountFromQueryParams(Uri uri) {
17446cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String name = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
17456cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String type = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
1746f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
1747f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            return null;
1748f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        }
1749f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        return new Account(name, type);
1750f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    }
1751f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
1752ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
17534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
17544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
175500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
175635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
175700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
175800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
175900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
176035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
176135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().update(db, values, selection, selectionArgs);
176235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1763d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            // TODO(emillar): We will want to disallow editing the contacts table at some point.
1764d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1765d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                count = db.update(Tables.CONTACTS, values, selection, selectionArgs);
176600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
176700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
176800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
1769d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
1770d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                count = updateContactData(db, ContentUris.parseId(uri), values);
1771c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
1772c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
1773c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
177420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
1775321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                count = updateData(uri, values, selection, selectionArgs);
177620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
177720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
1778c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
177920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA_ID: {
1780321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                count = updateData(uri, values, selection, selectionArgs);
178100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
178200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
17837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
17855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                count = db.update(Tables.RAW_CONTACTS, values, selection, selectionArgs);
17867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
17877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
179033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
179133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                count = updateRawContact(rawContactId, values, selection, selectionArgs);
17927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
17937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1795ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1796ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selection, selectionArgs);
1797ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1798ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1799ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1800ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1801ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1802ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1803ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String selectionWithId = (Groups._ID + "=" + groupId + " ")
1804ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + (selection == null ? "" : " AND " + selection);
1805ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selectionWithId, selectionArgs);
1806ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1807d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                // If changing visibility, then update contacts
1808ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (values.containsKey(Groups.GROUP_VISIBLE)) {
1809ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    mOpenHelper.updateAllVisible();
1810ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1811ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1812ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1813ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1814ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1815127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1816127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                count = updateAggregationException(db, values);
1817b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1818b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1819b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
18207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
1821f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
182200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
182300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
182400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        if (count > 0) {
182500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            getContext().getContentResolver().notifyChange(uri, null);
182600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
182700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
18284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
18294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
18305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values, String selection,
183133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            String[] selectionArgs) {
183233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
18335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        String selectionWithId = (RawContacts._ID + " = " + rawContactId + " ")
183433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                + (selection == null ? "" : " AND " + selection);
18355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        return db.update(Tables.RAW_CONTACTS, values, selectionWithId, selectionArgs);
183633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
183733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
1838321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
183920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            String[] selectionArgs) {
184020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
184120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
184220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        db.beginTransaction();
184320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
184420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Note that the query will return data according to the access restrictions,
184520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // so we don't need to worry about deleting data we don't have permission to read.
1846321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            Cursor c = query(uri, DataIdQuery.COLUMNS, selection, selectionArgs, null);
184720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            try {
184820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                while(c.moveToNext()) {
1849321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                    final long dataId = c.getLong(DataIdQuery._ID);
1850321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                    final long rawContactId = c.getLong(DataIdQuery.RAW_CONTACT_ID);
1851321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                    final String mimetype = c.getString(DataIdQuery.MIMETYPE);
1852321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                    count += updateData(dataId, rawContactId, mimetype, values);
185320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
185420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            } finally {
185520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                c.close();
185620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
185720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.setTransactionSuccessful();
185820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
185920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            db.endTransaction();
186020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
186120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
186220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
186320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
186420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1865321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(long dataId, long rawContactId, String mimeType, ContentValues values) {
186620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
186720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
186820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
186920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
187020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
18715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
187220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
187320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
187420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
187520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
187620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
187720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
187820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
187920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
188070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
188170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
188220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
188320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
188420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
188570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
188620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
188770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
188820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
188970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
189020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
189170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
189220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
189320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
189420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (containsIsSuperPrimary) {
189520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            setIsSuperPrimary(dataId);
189620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            setIsPrimary(dataId);
189720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
189820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Now that we've taken care of setting these, remove them from "values".
189970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
190020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (containsIsPrimary) {
190170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                mValues.remove(Data.IS_PRIMARY);
190220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
190320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } else if (containsIsPrimary) {
190420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            setIsPrimary(dataId);
190520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
190620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            // Now that we've taken care of setting this, remove it from "values".
190770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
190820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
190920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1910321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        resolveGroupSourceIdInValues(rawContactId, mimeType, db, mValues, false /* isInsert */);
1911321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
191270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (mValues.size() > 0) {
191370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return db.update(Tables.DATA, mValues, Data._ID + " = " + dataId, null);
191420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
191520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return 0;
191620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
191720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1918321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private void resolveGroupSourceIdInValues(long rawContactId, String mimeType, SQLiteDatabase db,
1919321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            ContentValues values, boolean isInsert) {
1920321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
1921321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1922321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1923321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            if (containsGroupSourceId && containsGroupId) {
1924321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                throw new IllegalArgumentException(
1925321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1926321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                                + "and GroupMembership.GROUP_ROW_ID");
1927321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            }
1928321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
1929321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            if (!containsGroupSourceId && !containsGroupId) {
1930321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                if (isInsert) {
1931321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                    throw new IllegalArgumentException(
1932321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1933321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                                    + "and GroupMembership.GROUP_ROW_ID");
1934321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                } else {
1935321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                    return;
1936321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                }
1937321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            }
1938321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
1939321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            if (containsGroupSourceId) {
1940321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1941321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                final long groupId = getOrMakeGroup(db, rawContactId, sourceId);
1942321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                values.remove(GroupMembership.GROUP_SOURCE_ID);
1943321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1944321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana            }
1945321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
1946321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
1947321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
1948d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private int updateContactData(SQLiteDatabase db, long contactId, ContentValues values) {
1949d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1950d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // First update all constituent contacts
1951f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        ContentValues optionValues = new ContentValues(5);
19526cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        OpenHelper.copyStringValue(optionValues, RawContacts.CUSTOM_RINGTONE,
1953d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
19546cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, RawContacts.SEND_TO_VOICEMAIL,
1955d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
19566cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, RawContacts.LAST_TIME_CONTACTED,
1957d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
19586cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, RawContacts.TIMES_CONTACTED,
1959d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
19606cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, RawContacts.STARRED,
1961d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
1962d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1963d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
1964d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (optionValues.size() == 0) {
1965d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
1966d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1967d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
19685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        db.update(Tables.RAW_CONTACTS, optionValues,
1969d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null);
1970d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        return db.update(Tables.CONTACTS, values, Contacts._ID + "=" + contactId, null);
1971f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
1972d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1973d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    public void updateContactTime(long contactId, long lastTimeContacted) {
1974f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
1975d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(2, contactId);
1976f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.execute();
1977d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1978d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
19795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static class RawContactPair {
19805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        final long rawContactId1;
19815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        final long rawContactId2;
1982127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1983127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        /**
19845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov         * Constructor that ensures that this.rawContactId1 &lt; this.rawContactId2
1985127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         */
19865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public RawContactPair(long rawContactId1, long rawContactId2) {
19875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            if (rawContactId1 < rawContactId2) {
19885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                this.rawContactId1 = rawContactId1;
19895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                this.rawContactId2 = rawContactId2;
1990127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
19915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                this.rawContactId2 = rawContactId1;
19925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                this.rawContactId1 = rawContactId2;
1993127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1994127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1995127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    }
199680c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1997127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
1998127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
1999d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        long contactId = values.getAsInteger(AggregationExceptions.CONTACT_ID);
20005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        long rawContactId = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID);
200180c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
20023cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        // First, we build a list of rawContactID-rawContactID pairs for the given contact.
20035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        ArrayList<RawContactPair> pairs = new ArrayList<RawContactPair>();
2004d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, RawContacts.CONTACT_ID
2005d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + "=" + contactId, null, null, null, null);
2006127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        try {
2007127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            while (c.moveToNext()) {
20085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long aggregatedContactId = c.getLong(ContactsQuery.RAW_CONTACT_ID);
20095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (aggregatedContactId != rawContactId) {
20105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    pairs.add(new RawContactPair(aggregatedContactId, rawContactId));
2011e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                }
2012b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2013b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        } finally {
2014b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            c.close();
2015b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
2016127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2017127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // Now we iterate through all contact pairs to see if we need to insert/delete/update
2018127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // the corresponding exception
2019127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ContentValues exceptionValues = new ContentValues(3);
2020127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
20215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        for (RawContactPair pair : pairs) {
2022127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            final String whereClause =
20235ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    AggregationExceptionColumns.RAW_CONTACT_ID1 + "=" + pair.rawContactId1 + " AND "
20245ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + AggregationExceptionColumns.RAW_CONTACT_ID2 + "=" + pair.rawContactId2;
2025127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
2026127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.delete(Tables.AGGREGATION_EXCEPTIONS, whereClause, null);
2027127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
20285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.RAW_CONTACT_ID1, pair.rawContactId1);
20295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.RAW_CONTACT_ID2, pair.rawContactId2);
2030127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
2031127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        exceptionValues);
2032127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
2033127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
2034127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
20355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        int aggregationMode = mContactAggregator.markContactForAggregation(rawContactId);
20366cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        if (aggregationMode != RawContacts.AGGREGATION_MODE_DISABLED) {
20375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            mContactAggregator.aggregateContact(db, rawContactId);
2038f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC
2039f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                    || exceptionType == AggregationExceptions.TYPE_KEEP_OUT) {
2040d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                mContactAggregator.updateAggregateData(contactId);
2041f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            }
20427a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        }
2043127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2044127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
2045127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
2046127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
2047b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
2048b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2049619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2050619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list.
2051619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2052619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private boolean isContained(String[] array, String value) {
2053bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array != null) {
2054bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            for (String test : array) {
2055bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                if (value.equals(test)) {
2056bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                    return true;
2057bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                }
2058619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2059619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2060619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return false;
2061619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2062619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2063619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2064619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list, and add to the
2065619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * array if the value doesn't already appear.
2066619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2067619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private String[] assertContained(String[] array, String value) {
206800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        if (array != null && !isContained(array, value)) {
2069619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            String[] newArray = new String[array.length + 1];
2070619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            System.arraycopy(array, 0, newArray, 0, array.length);
2071619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            newArray[array.length] = value;
2072619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            array = newArray;
2073619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2074619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return array;
2075619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2076619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
20774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
20784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
20794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
20804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
208135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2082d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
20831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
2084c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
2085c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2086d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        String contactIdColName = Tables.CONTACTS + "." + Contacts._ID;
20874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2088619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
2089619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
2090a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
20914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
209235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
209335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
209435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
209535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2096d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2097d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS);
209800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsRestrictions(qb);
209900ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsPrimaryRestrictions(sContactsProjectionMap);
2100d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsProjectionMap);
2101619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
2102619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2103619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2104d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2105619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = ContentUris.parseId(uri);
2106d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS);
2107d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.appendWhere(ContactsColumns.CONCRETE_ID + "=" + aggId + " AND ");
210800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsRestrictions(qb);
210900ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsPrimaryRestrictions(sContactsProjectionMap);
2110d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsProjectionMap);
21116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
21126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
21136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
2114d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_SUMMARY: {
2115619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
2116d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE);
211700ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsRestrictions(qb);
211800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsPrimaryRestrictions(sContactsSummaryProjectionMap);
2119d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                projection = assertContained(projection, Contacts.PRIMARY_PHONE_ID);
2120d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsSummaryProjectionMap);
2121d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                groupBy = contactIdColName;
21221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
21231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
21241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2125d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_SUMMARY_ID: {
2126619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
21271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                long aggId = ContentUris.parseId(uri);
2128d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE);
2129d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.appendWhere(ContactsColumns.CONCRETE_ID + "=" + aggId + " AND ");
213000ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsRestrictions(qb);
213100ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsPrimaryRestrictions(sContactsSummaryProjectionMap);
2132d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                projection = assertContained(projection, Contacts.PRIMARY_PHONE_ID);
2133d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsSummaryProjectionMap);
2134d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                groupBy = contactIdColName;
21351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
21361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
21371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2138d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_SUMMARY_FILTER: {
2139619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
2140d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE);
2141d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsSummaryProjectionMap);
2142ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
2143d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    qb.appendWhere(buildContactLookupWhereClause(uri.getLastPathSegment()));
2144ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
2145d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                groupBy = contactIdColName;
2146ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2147ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2148ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2149d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_SUMMARY_STREQUENT_FILTER:
2150d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_SUMMARY_STREQUENT: {
2151d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the first query for starred
2152d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE);
2153d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsSummaryProjectionMap);
2154d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                if (match == CONTACTS_SUMMARY_STREQUENT_FILTER
2155d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
2156d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    qb.appendWhere(buildContactLookupWhereClause(uri.getLastPathSegment()));
2157d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
2158d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                final String starredQuery = qb.buildQuery(projection, Contacts.STARRED + "=1",
2159d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        null, contactIdColName, null, null,
2160d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        null /* limit */);
2161d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
2162d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
2163d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
2164d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE);
2165d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsSummaryProjectionMap);
2166d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                if (match == CONTACTS_SUMMARY_STREQUENT_FILTER
2167d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
2168d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    qb.appendWhere(buildContactLookupWhereClause(uri.getLastPathSegment()));
2169d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
2170d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
2171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
2172d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
2173d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        null, contactIdColName, null, null, null);
2174d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
2175d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
2176d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
2177d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
2178d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                Cursor c = db.rawQueryWithFactory(null, query, null,
2179d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Tables.CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE);
2180d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
2181d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if ((c != null) && !isTemporary()) {
2182d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
2183d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
2184d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
2185d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
2186d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
2187d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
2188d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_SUMMARY_GROUP: {
2189d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.CONTACTS_JOIN_PRESENCE_PRIMARY_PHONE);
219000ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsRestrictions(qb);
219100ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsPrimaryRestrictions(sContactsSummaryProjectionMap);
2192d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                projection = assertContained(projection, Contacts.PRIMARY_PHONE_ID);
2193d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sContactsSummaryProjectionMap);
2194b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
2195d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    qb.appendWhere(" AND " + sContactsInGroupSelect);
2196b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    selectionArgs = appendGroupArg(selectionArgs, uri.getLastPathSegment());
2197b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
2198d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                groupBy = contactIdColName;
2199b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
2200b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
2201b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
2202d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
2203619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = Long.parseLong(uri.getPathSegments().get(1));
2204d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS_GROUPS);
2205d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.appendWhere(RawContacts.CONTACT_ID + "=" + aggId + " AND ");
2206619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
220700ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsPrimaryRestrictions(sDataRawContactsGroupsContactProjectionMap);
220800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                qb.setProjectionMap(sDataRawContactsGroupsContactProjectionMap);
22096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
22106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
221100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
22122815f58f72f109790585931f601a63ddc02536a5Evan Millar            case CONTACTS_RAW_CONTACTS: {
22132815f58f72f109790585931f601a63ddc02536a5Evan Millar                long contactId = Long.parseLong(uri.getPathSegments().get(1));
22142815f58f72f109790585931f601a63ddc02536a5Evan Millar                qb.setTables(Tables.RAW_CONTACTS_JOIN_CONTACTS);
22152815f58f72f109790585931f601a63ddc02536a5Evan Millar                qb.setProjectionMap(sRawContactsContactsProjectionMap);
22162815f58f72f109790585931f601a63ddc02536a5Evan Millar                qb.appendWhere(RawContacts.CONTACT_ID + "=" + contactId + " AND ");
22172815f58f72f109790585931f601a63ddc02536a5Evan Millar                applyDataRestrictionExceptions(qb);
22182815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
22192815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
22202815f58f72f109790585931f601a63ddc02536a5Evan Millar
2221ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
2222d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS);
22230c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar                qb.setProjectionMap(sDataRawContactsContactProjectionMap);
2224ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
2225ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
2226d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    qb.appendWhere(" AND " + buildContactLookupWhereClause(
2227ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                            uri.getLastPathSegment()));
2228ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
2229ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2230ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2231ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2232ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES: {
2233d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS);
22340c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar                qb.setProjectionMap(sDataRawContactsContactProjectionMap);
2235ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Phone.CONTENT_ITEM_TYPE + "\"");
2236ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2237ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2238ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2239ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
2240d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS);
22410c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar                qb.setProjectionMap(sDataRawContactsContactProjectionMap);
224267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                qb.appendWhere(Data.MIMETYPE + " = \"" + StructuredPostal.CONTENT_ITEM_TYPE + "\"");
2243ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2244ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2245ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
22465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
22475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.setTables(Tables.RAW_CONTACTS);
2248d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sRawContactsProjectionMap);
224900ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsRestrictions(qb);
22504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
22514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
22524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
22545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
22555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.setTables(Tables.RAW_CONTACTS);
2256d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sRawContactsProjectionMap);
22575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.appendWhere(RawContactsColumns.CONCRETE_ID + "=" + rawContactId + " AND ");
225800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey                applyContactsRestrictions(qb);
22594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
22604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
22614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
22635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
22645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS);
22650c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar                qb.setProjectionMap(sDataRawContactsGroupsProjectionMap);
22665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.appendWhere(Data.RAW_CONTACT_ID + "=" + rawContactId + " AND ");
2267619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
2268a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2269a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2270a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
227128ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey            case CONTACTS_FILTER_EMAIL: {
2272619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
2273d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS);
22740c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar                qb.setProjectionMap(sDataRawContactsProjectionMap);
22755d0f923eb4c5351ebf323cc6f19c82acff98693eJeff Sharkey                qb.appendWhere(Data.MIMETYPE + "='" + CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'");
2276e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhere(" AND " + CommonDataKinds.Email.DATA + "=");
2277e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhereEscapeString(uri.getPathSegments().get(2));
2278e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
2279e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
2280e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
2281e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
22826cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
22836cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
2284343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                if (!TextUtils.isEmpty(accountName)) {
2285226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    qb.appendWhere(RawContactsColumns.CONCRETE_ACCOUNT_NAME + "="
2286035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountName) + " AND "
2287226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                            + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + "="
2288035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountType) + " AND ");
2289343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                }
22905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS);
22919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
2292619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
2293e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
2294e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
2295e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
22964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
22975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.setTables(Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS);
22989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
2299ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri) + " AND ");
2300619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
23014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
23024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
23034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2304a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
2305619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
2306a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
2307a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
2308a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
23095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    sortOrder = Data.RAW_CONTACT_ID;
2310a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
2311a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2312a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                final String number = uri.getLastPathSegment();
2313bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                OpenHelper.buildPhoneLookupQuery(qb, number);
23140c83a3c4b6a9c8a0dfa0b3aa2af91b74d8e3304fEvan Millar                qb.setProjectionMap(sDataRawContactsProjectionMap);
2315a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2316a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2317a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2318ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2319ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
2320ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
2321ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2322ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2323ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2324ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
2326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
2327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
2328ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(GroupsColumns.CONCRETE_ID + "=" + groupId);
2329ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2330ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2331ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2332ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
2333d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setTables(Tables.GROUPS_JOIN_PACKAGES_DATA_RAW_CONTACTS_CONTACTS);
2334ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
2335ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                groupBy = GroupsColumns.CONCRETE_ID;
2336ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2337ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2338ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2339b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
23405ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS_JOIN_RAW_CONTACTS);
2341b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
2342b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2343b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2344b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
234531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
2346d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
2347c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2348c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                // TODO drop MAX_SUGGESTIONS in favor of LIMIT
234931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final String maxSuggestionsParam =
235031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        uri.getQueryParameter(AggregationSuggestions.MAX_SUGGESTIONS);
235131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
235231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
235331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                if (maxSuggestionsParam != null) {
235431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(maxSuggestionsParam);
235531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
235631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
235731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
235831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2359d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(contactId, projection,
2360d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        sContactsProjectionMap, maxSuggestions);
236131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
236231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
23635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case PRESENCE: {
2364373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setTables(Tables.PRESENCE);
2365373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setProjectionMap(sPresenceProjectionMap);
23665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
23675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
23685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
23695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case PRESENCE_ID: {
2370373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setTables(Tables.PRESENCE);
2371373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setProjectionMap(sPresenceProjectionMap);
2372373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.appendWhere(Presence._ID + "=" + ContentUris.parseId(uri));
23735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
23745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
23755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
2376c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
2377a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
2378c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
2379c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2380c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
2381c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                // TODO
2382c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                break;
2383c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
2384c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
23854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
2386f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
2387c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
23884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
23894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
23904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Perform the query and set the notification uri
23911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final Cursor c = qb.query(db, projection, selection, selectionArgs,
2392bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy, null, sortOrder, limit);
23934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
23944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
23954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
23964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
23974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
23984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
23997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2400c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
2401c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
2402c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
2403c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
2404c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
2405c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private String getLimit(Uri url) {
2406c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limitParam = url.getQueryParameter("limit");
2407c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
2408c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
2409c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
2410c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
2411c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
2412c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
2413c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
2414c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
2415c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
2416c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
2417c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
2418c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
2419c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
2420c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
2421c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
2422c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
2423c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2424c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    /**
24256cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov     * List of package names with access to {@link RawContacts#IS_RESTRICTED} data.
242667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     */
242767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private static final String[] sAllowedPackages = new String[] {
242867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        "com.android.contacts",
242967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        "com.facebook",
243067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    };
243167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
243267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    /**
243367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     * Check if {@link Binder#getCallingUid()} should be allowed access to
24346cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov     * {@link RawContacts#IS_RESTRICTED} data.
243567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey     */
243667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private boolean hasRestrictedAccess() {
243767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final PackageManager pm = getContext().getPackageManager();
243867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String[] callerPackages = pm.getPackagesForUid(Binder.getCallingUid());
243967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
244067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        // Has restricted access if caller matches any packages
244167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        for (String callerPackage : callerPackages) {
244267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            for (String allowedPackage : sAllowedPackages) {
244367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                if (allowedPackage.equals(callerPackage)) {
244467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    return true;
244567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                }
244667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            }
244767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
244867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        return false;
244967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
245067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
245167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    /**
2452619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the calling process, and
2453d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * add projections to correctly select {@link Contacts#PRIMARY_PHONE_ID}
2454d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * and {@link Contacts#PRIMARY_EMAIL_ID}.
2455619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
245600ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    private void applyContactsPrimaryRestrictions(HashMap<String, String> projection) {
245767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        String projectionPhone;
245867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        String projectionEmail;
245967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
246067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
246167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // With restricted access, always give optimal values
2462d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            projectionPhone = ContactsColumns.OPTIMAL_PRIMARY_PHONE_ID + " AS "
2463d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    + Contacts.PRIMARY_PHONE_ID;
2464d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            projectionEmail = ContactsColumns.OPTIMAL_PRIMARY_EMAIL_ID + " AS "
2465d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    + Contacts.PRIMARY_EMAIL_ID;
246667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
246767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            // With general access, always give fallback values
2468d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            projectionPhone = ContactsColumns.FALLBACK_PRIMARY_PHONE_ID + " AS "
2469d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    + Contacts.PRIMARY_PHONE_ID;
2470d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            projectionEmail = ContactsColumns.FALLBACK_PRIMARY_EMAIL_ID + " AS "
2471d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                    + Contacts.PRIMARY_EMAIL_ID;
247267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
247367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2474d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        projection.remove(Contacts.PRIMARY_PHONE_ID);
2475d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        projection.put(Contacts.PRIMARY_PHONE_ID, projectionPhone);
2476619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2477d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        projection.remove(Contacts.PRIMARY_EMAIL_ID);
2478d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        projection.put(Contacts.PRIMARY_EMAIL_ID, projectionEmail);
2479619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2480619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2481619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
248200ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey     * Restrict selection of {@link Contacts} to only public ones, or those
248300ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey     * the caller has been granted an exception to.
2484619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
248500ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    private void applyContactsRestrictions(SQLiteQueryBuilder qb) {
248600ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        qb.appendWhere(getContactsRestrictions());
248770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
248870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
248900ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
249067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (hasRestrictedAccess()) {
249170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
249270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
24936cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
249470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
249570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
249670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
249770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
249870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (hasRestrictedAccess()) {
249970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
250067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
25015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
25025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
2503619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2504619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2505619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2506619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2507619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
2508619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
2509619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
2510619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
2511e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    void applyDataRestrictionExceptions(SQLiteQueryBuilder qb) {
251200ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        applyContactsRestrictions(qb);
2513619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
2514619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2515619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
25167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
25177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
25187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
25197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    private static class ContactsEntityIterator implements EntityIterator {
25207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
25217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
25227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
25247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
25257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
25267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
25277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
25287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
25297a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
25307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
25317a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
25327a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
25337a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
25347a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
25357a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
25367a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
25377a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
25387a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
25397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
25407a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
25417a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
25427a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4};
25437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
25456cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
25466cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
25476cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.SOURCE_ID,
25486cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.VERSION,
25496cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.DIRTY,
25507a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data._ID,
25517a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RES_PACKAGE,
25527a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.MIMETYPE,
25537a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
25547a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
25557a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
25567a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
25577a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
25587a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
25597a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
25607a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
25617a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
25627a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
25637a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
25647a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
25657a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
25667a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
25677a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
25687a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
25697a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
25707a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
25717a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4,
25727a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RAW_CONTACT_ID,
25737a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.IS_PRIMARY,
25747a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA_VERSION,
25757a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                GroupMembership.GROUP_SOURCE_ID,
25767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC1,
25777a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC2,
25787a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC3,
25797a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC4};
2580035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
2581035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
2582035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
2583035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
2584035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
2585035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
2586035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
258767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
258867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
258967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
25907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_RAW_CONTACT_ID = 27;
25917a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_IS_PRIMARY = 28;
25927a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_DATA_VERSION = 29;
25937a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_GROUP_SOURCE_ID = 30;
25947a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 31;
25957a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 32;
25967a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 33;
25977a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 34;
25987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public ContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
26007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
26017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
26027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
26047a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                    ? Data.RAW_CONTACT_ID
26057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                    : (Data.RAW_CONTACT_ID + "," + sortOrder);
26067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
26087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
2609226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setTables(Tables.CONTACT_ENTITIES);
26107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
26115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.appendWhere(Data.RAW_CONTACT_ID + "=" + contactsIdString);
26127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
26136cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
26146cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
2615035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
26166cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
2617035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
26186cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                        + RawContacts.ACCOUNT_TYPE + "="
2619035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
2620035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
26217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
26227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
26237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
26247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
26257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
26277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
26287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
26297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
26307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
26317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
26327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
26337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
26357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
26367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
26377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
26387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
26407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
26417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
26437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
26447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
26457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
26467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
26477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
26487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
26497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
26517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26527a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
26537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
26557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
26566cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
26576cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
26585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
26596cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
26606cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
26616cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
26627a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
26637a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
26647a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
26657a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
26667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
26677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
26697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
26707a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
26717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
26727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
26737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
26747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
26757a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data._ID, c.getString(COLUMN_DATA_ID));
26767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
26777a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
26787a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.IS_PRIMARY, c.getString(COLUMN_IS_PRIMARY));
26797a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
26809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
26819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
26829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
26839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
26847a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
26857a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                for (int i = 0; i < DATA_KEYS.length; i++) {
26867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
26877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
26887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
26897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
26907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
26917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
26927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
26937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
26947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
26957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
26967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
26977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
26987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
26997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
27007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
27017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
27027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
27047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
27057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
27067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2707226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    /**
2708226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
2709226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
2710226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     */
2711226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    private static class GroupsEntityIterator implements EntityIterator {
2712226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private final Cursor mEntityCursor;
2713226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private volatile boolean mIsClosed;
2714226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2715226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final String[] PROJECTION = new String[]{
2716226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups._ID,
2717226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_NAME,
2718226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_TYPE,
2719226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.SOURCE_ID,
2720226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.DIRTY,
2721226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.VERSION,
2722226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.RES_PACKAGE,
2723226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE,
2724226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE_RES,
27257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.GROUP_VISIBLE,
27267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC1,
27277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC2,
27287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC3,
27297a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC4,
27307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYSTEM_ID,
27317a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.NOTES};
2732226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2733226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ID = 0;
2734226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_NAME = 1;
2735226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 2;
2736226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_SOURCE_ID = 3;
2737226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_DIRTY = 4;
2738226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_VERSION = 5;
2739226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_RES_PACKAGE = 6;
2740226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE = 7;
2741226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE_RES = 8;
2742226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_GROUP_VISIBLE = 9;
27437a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 10;
27447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 11;
27457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 12;
27467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 13;
27477a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYSTEM_ID = 14;
27487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_NOTES = 15;
2749226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2750226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
2751226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
2752226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = false;
2753226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2754226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String updatedSortOrder = (sortOrder == null)
2755226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    ? Groups._ID
2756226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    : (Groups._ID + "," + sortOrder);
2757226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2758226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
2759226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
2760226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
2761226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setProjectionMap(sGroupsProjectionMap);
2762226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (groupIdString != null) {
2763226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups._ID + "=" + groupIdString);
2764226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
2765226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountName = uri.getQueryParameter(Groups.ACCOUNT_NAME);
2766226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountType = uri.getQueryParameter(Groups.ACCOUNT_TYPE);
2767226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!TextUtils.isEmpty(accountName)) {
2768226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups.ACCOUNT_NAME + "="
2769226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
2770226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + Groups.ACCOUNT_TYPE + "="
2771226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
2772226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
2773226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
2774226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    null, null, updatedSortOrder);
2775226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToFirst();
2776226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
2777226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2778226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public void close() {
2779226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
2780226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("closing when already closed");
2781226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
2782226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = true;
2783226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.close();
2784226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
2785226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2786226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public boolean hasNext() throws RemoteException {
2787226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
2788226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
2789226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
2790226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2791226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return !mEntityCursor.isAfterLast();
2792226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
2793226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2794226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public Entity next() throws RemoteException {
2795226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
2796226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
2797226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
2798226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!hasNext()) {
2799226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
2800226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
2801226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2802226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
2803226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2804226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final long groupId = c.getLong(COLUMN_ID);
2805226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2806226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            // we expect the cursor is already at the row we need to read from
2807226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            ContentValues groupValues = new ContentValues();
2808226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
2809226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
2810226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups._ID, groupId);
2811226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
2812226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
2813226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
2814226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
2815226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
2816226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
2817226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
28187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
28197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
28207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
28217a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
28227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
28237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
2824226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            Entity group = new Entity(groupValues);
2825226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2826226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToNext();
2827226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2828226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return group;
2829226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
2830226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    }
2831226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2832a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
28337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
28347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
28357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
28367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
28375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS:
28385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID:
28397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
28405ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (match == RAW_CONTACTS_ID) {
28417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
28427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
28437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
28447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                return new ContactsEntityIterator(this, contactsIdString,
28457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
2846226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS:
2847226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS_ID:
2848226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String idString = null;
2849226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                if (match == GROUPS_ID) {
2850226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    idString = uri.getPathSegments().get(1);
2851226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                }
2852226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
2853226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                return new GroupsEntityIterator(this, idString,
2854226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        uri, selection, selectionArgs, sortOrder);
28557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
28567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
28577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
28587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
28597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
28604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
28614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
2862a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
28634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
2864d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: return Contacts.CONTENT_TYPE;
2865d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: return Contacts.CONTENT_ITEM_TYPE;
28665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: return RawContacts.CONTENT_TYPE;
28675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: return RawContacts.CONTENT_ITEM_TYPE;
2868508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
28696bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
2870508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2871b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return mOpenHelper.getDataMimeType(dataId);
287231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: return AggregationExceptions.CONTENT_TYPE;
287331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTION_ID: return AggregationExceptions.CONTENT_ITEM_TYPE;
2874d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: return Contacts.CONTENT_TYPE;
2875c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
2876c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
2877c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
2878c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
28794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
2880a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        throw new UnsupportedOperationException("Unknown uri: " + uri);
28814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
28827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2883b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    @Override
28847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
28857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            throws OperationApplicationException {
28867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
28877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
28887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        db.beginTransaction();
28897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        try {
28907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentProviderResult[] results = super.applyBatch(operations);
28917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.setTransactionSuccessful();
28927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return results;
28937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        } finally {
28947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.endTransaction();
28957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
28967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
2897c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
28985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName) {
28993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
29003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mContactDisplayNameUpdate.bindString(1, displayName);
29013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
29023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mContactDisplayNameUpdate.bindNull(1);
29033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
29045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mContactDisplayNameUpdate.bindLong(2, rawContactId);
29053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mContactDisplayNameUpdate.execute();
29063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
29073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2908c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
2909c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
2910c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
2911c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
2912c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
2913c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
2914c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsPrimary(long dataId) {
2915c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
2916c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(2, dataId);
2917c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(3, dataId);
2918c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
2919c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
2920c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
2921c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
2922c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
2923c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
2924c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
2925c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
2926c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
2927c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsSuperPrimary(long dataId) {
2928c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
2929c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(2, dataId);
2930c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(3, dataId);
2931c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
2932619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2933619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the parent aggregate and package for this new primary
2934619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2935619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2936619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long aggId = -1;
2937619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isRestricted = false;
2938619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        String mimeType = null;
2939619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2940619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        Cursor cursor = null;
2941619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        try {
2942d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            cursor = db.query(DataRawContactsQuery.TABLE, DataRawContactsQuery.PROJECTION,
294367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                    DataColumns.CONCRETE_ID + "=" + dataId, null, null, null, null);
2944619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor.moveToFirst()) {
2945d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                aggId = cursor.getLong(DataRawContactsQuery.CONTACT_ID);
2946d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                isRestricted = (cursor.getInt(DataRawContactsQuery.IS_RESTRICTED) == 1);
2947d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                mimeType = cursor.getString(DataRawContactsQuery.MIMETYPE);
2948619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2949619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } finally {
2950619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor != null) {
2951619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                cursor.close();
2952619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2953619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2954619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2955619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Bypass aggregate update if no parent found, or if we don't keep track
2956619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // of super-primary for this mimetype.
2957d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (aggId == -1) {
2958d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return;
2959d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2960619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2961619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isPhone = CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
2962619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isEmail = CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimeType);
2963619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2964619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Record this value as the new primary for the parent aggregate
2965619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final ContentValues values = new ContentValues();
2966619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (isPhone) {
2967d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            values.put(ContactsColumns.OPTIMAL_PRIMARY_PHONE_ID, dataId);
2968d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            values.put(ContactsColumns.OPTIMAL_PRIMARY_PHONE_IS_RESTRICTED, isRestricted);
2969619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (isEmail) {
2970d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            values.put(ContactsColumns.OPTIMAL_PRIMARY_EMAIL_ID, dataId);
2971d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            values.put(ContactsColumns.OPTIMAL_PRIMARY_EMAIL_IS_RESTRICTED, isRestricted);
2972619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2973619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2974619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // If this data is unrestricted, then also set as fallback
2975619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (!isRestricted && isPhone) {
2976d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            values.put(ContactsColumns.FALLBACK_PRIMARY_PHONE_ID, dataId);
2977619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (!isRestricted && isEmail) {
2978d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            values.put(ContactsColumns.FALLBACK_PRIMARY_EMAIL_ID, dataId);
2979619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2980619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2981d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        // Push update into contacts table, if needed
2982619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (values.size() > 0) {
2983d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            db.update(Tables.CONTACTS, values, Contacts._ID + "=" + aggId, null);
2984619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2985c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
2986ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2987d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private String buildContactLookupWhereClause(String filterParam) {
2988ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        StringBuilder filter = new StringBuilder();
2989d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        filter.append(Tables.CONTACTS);
2990ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(".");
2991d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        filter.append(Contacts._ID);
2992ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" IN (SELECT ");
2993d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        filter.append(RawContacts.CONTACT_ID);
2994ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" FROM ");
29955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        filter.append(Tables.RAW_CONTACTS);
2996ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" WHERE ");
29976cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        filter.append(RawContacts._ID);
2998445ca81effbb0d61c7bc0033acf2d3dfd228fd4eDmitri Plotnikov        filter.append(" IN ");
2999c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(filter, filterParam, null);
3000445ca81effbb0d61c7bc0033acf2d3dfd228fd4eDmitri Plotnikov        filter.append(")");
3001445ca81effbb0d61c7bc0033acf2d3dfd228fd4eDmitri Plotnikov        return filter.toString();
3002445ca81effbb0d61c7bc0033acf2d3dfd228fd4eDmitri Plotnikov    }
3003445ca81effbb0d61c7bc0033acf2d3dfd228fd4eDmitri Plotnikov
30045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
3005c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
3006c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
3007c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
3008c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
3009c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3010a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
3011c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            String limit) {
3012c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append("(SELECT DISTINCT raw_contact_id FROM name_lookup WHERE normalized_name GLOB '");
3013c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
3014c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append("*'");
3015c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limit != null) {
3016c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
3017c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
3018c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(")");
3019ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
3020ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3021b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private String[] appendGroupArg(String[] selectionArgs, String arg) {
3022b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
3023b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
3024b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
3025b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
3026b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
3027b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            System.arraycopy(selectionArgs, 0, newSelectionArgs, 0, selectionArgs.length);
3028b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            newSelectionArgs[newLength - 1] = arg;
3029b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
3030b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
3031b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
30324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
3033