ContactsProvider2.java revision d60cf9ba1d039f1a22375f56c18356e0d4f8ca14
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
193de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.internal.content.SyncStateContentProviderHelper;
203de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
213de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.AggregatedPresenceColumns;
223de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.AggregationExceptionColumns;
233de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.Clauses;
243de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.ContactsColumns;
253de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.DataColumns;
263de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.DisplayNameSources;
273de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.GroupsColumns;
283de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.MimetypesColumns;
293de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.NameLookupColumns;
303de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.NameLookupType;
31f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport com.android.providers.contacts.OpenHelper.NicknameLookupColumns;
323de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PhoneColumns;
333de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
343de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PresenceColumns;
353de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.RawContactsColumns;
363de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.SettingsColumns;
37a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.android.providers.contacts.OpenHelper.StatusUpdatesColumns;
383de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.Tables;
39a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Lists;
40a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Maps;
41a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Sets;
423de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
43b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
44caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
4570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wongimport android.accounts.OnAccountsUpdatedListener;
46c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
47568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
48568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
4935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
5235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
5367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.EntityIterator;
54568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
5667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
573de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.content.SharedPreferences.Editor;
58b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
60ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
61a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
62b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
63b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
66c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
68d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
69b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
700e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
71d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.pim.vcard.VCardComposer;
723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
73508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
743de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
753de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.LiveFolders;
763de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.OpenableColumns;
773de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.SyncStateContract;
78b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
793de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
803de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
813de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
823de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
833de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Presence;
843de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
87ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
88ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
92de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
93b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
944097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
96a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
97a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
98f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Token;
99f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Tokenizer;
100c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
102d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
103b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
104d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
105d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
106f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport java.lang.ref.SoftReference;
1077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
109b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1100e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
112622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
113b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1140e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
115ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
12170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wongpublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdatedListener {
122caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
123bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
124bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
125bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
127619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
128619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
129619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1333d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
1343d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * Shared preference key for the legacy contact import version. The need for a version
1353d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
1383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final String PREF_CONTACTS_IMPORTED = "contacts_imported_v1";
1393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int PREF_CONTACTS_IMPORT_VERSION = 1;
1403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1410e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1420e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
143a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
145d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
146d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.TIMES_CONTACTED + " DESC, "
1479b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
148d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
149d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
150d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
151d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
152d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
153d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
1624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
1664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1676bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1686bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
169ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
17048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
17148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
17248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
17348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
17448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
17548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
17648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
17748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
178a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
181b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
182b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
183b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE = 7000;
1851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE_ID = 7001;
1861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
18731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
18831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
189eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
190eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
191ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
192ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
193ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
194ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
19535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
196b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
19735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
198c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
199c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
200c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
201a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov    private static final int DATA_WITH_PRESENCE = 13000;
20219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
2031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2061b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2071b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface ContactsQuery {
2095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final String TABLE = Tables.RAW_CONTACTS;
2109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
21167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2126cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2136cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
2146cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
215ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
216ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2175ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
21867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_NAME = 1;
21967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_TYPE = 2;
22067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
22167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
222d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
223d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS;
22467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
22567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2266cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
228d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            ContactsColumns.CONCRETE_ID,
2293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.CONCRETE_ID,
230ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
231ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
232d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
23367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
234d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
23567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int MIMETYPE_ID = 3;
236ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DisplayNameQuery {
23967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
244f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
245a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            Organization.TITLE,
2463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 0;
2493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 1;
250a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        public static final int DATA = 2;
251a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        public static final int TITLE = 3;
2523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
25414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
25567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
25788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
262f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
26388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
26488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
26588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
26688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
26788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
26888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
26988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
270f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
27314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
277f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
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
288f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
289f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private interface NicknameLookupQuery {
290f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
291f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
292f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] COLUMNS = new String[] {
293f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
294f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        };
295f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
296f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int CLUSTER = 0;
297f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
298f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
29925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private static final HashMap<String, Integer> sDisplayNameSources;
3003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    static {
30125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources = new HashMap<String, Integer>();
30225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(StructuredName.CONTENT_ITEM_TYPE,
30325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.STRUCTURED_NAME);
304a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sDisplayNameSources.put(Nickname.CONTENT_ITEM_TYPE,
305a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                DisplayNameSources.NICKNAME);
30625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Organization.CONTENT_ITEM_TYPE,
30725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.ORGANIZATION);
30825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Phone.CONTENT_ITEM_TYPE,
30925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.PHONE);
31025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Email.CONTENT_ITEM_TYPE,
31125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.EMAIL);
3123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
31331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
314caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    public static final String DEFAULT_ACCOUNT_TYPE = "com.google.GAIA";
315df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
316caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
317038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
318038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
319e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
321ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains contacts and presence columns */
322ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    private static final HashMap<String, String> sContactsWithPresenceProjectionMap;
323ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
324d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
3254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
330e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
331ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
332ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
333ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
334ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
335373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
336b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
337eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
338eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
339373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains Presence columns */
340373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    private static final HashMap<String, String> sPresenceProjectionMap;
34119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    /** Contains Presence columns */
342a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov    private static final HashMap<String, String> sDataWithPresenceProjectionMap;
3431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
346b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /** Sql where statement for filtering on groups. */
347d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String sContactsInGroupSelect;
34819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
349c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
350c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
352c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
353d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Precompiled sql statement for incrementing times contacted for an contact */
354f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private SQLiteStatement mLastTimeContactedUpdate;
3553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
35625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
35773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /** Precompiled sql statement for marking a raw contact as dirty */
35873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private SQLiteStatement mRawContactDirtyUpdate;
359e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    /** Precompiled sql statement for updating an aggregated presence status */
360a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
361f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
362f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
363a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
364a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
365a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
366a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
367a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
3684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
3694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
370a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
371d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
372d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
373d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
3743653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
3753653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3762d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
3772d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3783653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
3795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
3805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
3815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
3825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
383ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
384ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
3855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
3863653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
3875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
3885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
3895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
390b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
3924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
393ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
39448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
3955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
396ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
3974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
39848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
3995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
4005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
4014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
402ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
40348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
4041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
405ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
406ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
407ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
408ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
40935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
410b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
411b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
41235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
413a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
414b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
415b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
416b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
417b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
419eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
420eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
421bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
422bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
4231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
424c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
425c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
426c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
427c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
428c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
429c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
430c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
4321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
4331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
4341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
4351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
4361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
4371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
4381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
4391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
44019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov        // Private API
441a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data_with_presence", DATA_WITH_PRESENCE);
44219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
44319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
44419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
445038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
446038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
447e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
4494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
4504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
4514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
4524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
4534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
4544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
4554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
4564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
457f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
4584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
4595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
460d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        sContactsProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
461d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
462d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        sContactsProjectionMap.put(OpenableColumns.SIZE, "0 AS " + OpenableColumns.SIZE);
4634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
464ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap = new HashMap<String, String>();
465ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap.putAll(sContactsProjectionMap);
466ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap.put(Contacts.PRESENCE_STATUS,
467a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                Tables.AGGREGATED_PRESENCE + "." + Presence.PRESENCE_STATUS
468a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + " AS " + Contacts.PRESENCE_STATUS);
469ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap.put(Contacts.PRESENCE_CUSTOM_STATUS,
470a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                StatusUpdatesColumns.STATUS
471a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + " AS " + Contacts.PRESENCE_CUSTOM_STATUS);
472a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        sContactsWithPresenceProjectionMap.put(Contacts.PRESENCE_CUSTOM_STATUS_TIMESTAMP,
473a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                StatusUpdatesColumns.TIMESTAMP
474a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + " AS " + Contacts.PRESENCE_CUSTOM_STATUS_TIMESTAMP);
4754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
4774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
4784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
4794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
4804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
4814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
4824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
4834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
4844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
4854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
4864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
4874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
4884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
4894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
4904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
4914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
4924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
4934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
4944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
4954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
4962815f58f72f109790585931f601a63ddc02536a5Evan Millar
4974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
4984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
4994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
5004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
5014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
5024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
5034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
5044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
5054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
5064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
5074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
5084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
5094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
5104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
5114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
5124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
5134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
5144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
5154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
5164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
5174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
5184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
5194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
5204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
5244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
53056d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
5384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
539a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
5415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
5425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
5435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
5445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
5455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
5465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
5475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
5485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
5495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
5505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
5515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
5525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
5535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
5545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
5555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
5565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
5575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
5585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
5595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
5605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
5615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
5625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
5635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
5645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
5685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5698f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
5775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
5785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
5795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
580e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
581e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
582e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
58356d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
58456d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar                Contacts.LOOKUP_KEY + " AS " + PhoneLookup.LOOKUP_KEY);
585e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
586e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
587e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
588e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
589e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
590e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
591e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
592e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
593e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
594e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
595e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
596e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
597e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
598e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
599e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
600e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
601e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
602e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
603e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
604e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
605e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
606e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
607e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
608e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
609e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
610e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
6119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
612e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        HashMap<String, String> columns;
6137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
614ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
615ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
61689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
617035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
618035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
6199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
6209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
6219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
62289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
623ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
62467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
625ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
6263cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
62794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
6283cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
62938446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
63089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
63189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
63289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
63389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
634ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
635ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
6366cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
637ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
638ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
639d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
640d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
641ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
642ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
643ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
644d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
645d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
646ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
647f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
648ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
649ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
650b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
651b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
652b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
653b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
6540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
6550c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
656b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
657b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
658eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
659eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
660eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
661eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
662eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
663eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
664341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
665341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
666341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
667fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
668341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
669341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
670341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
67168936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
67268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
67368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
67468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
67568936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
676e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
67768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
67868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
67968936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
680eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
681eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
682373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
683373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence._ID, Presence._ID);
6844dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
685373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.DATA_ID, Presence.DATA_ID);
686373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.IM_ACCOUNT, Presence.IM_ACCOUNT);
687373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.IM_HANDLE, Presence.IM_HANDLE);
6884dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(Presence.PROTOCOL, Presence.PROTOCOL);
68970c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
69070c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
69170c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        columns.put(Presence.CUSTOM_PROTOCOL, "(CASE WHEN " + Presence.CUSTOM_PROTOCOL
69270c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov                + "='' THEN NULL ELSE " + Presence.CUSTOM_PROTOCOL + " END) AS "
69370c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov                + Presence.CUSTOM_PROTOCOL);
694373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.PRESENCE_STATUS, Presence.PRESENCE_STATUS);
695a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        columns.put(Presence.PRESENCE_CUSTOM_STATUS,
696a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                StatusUpdatesColumns.STATUS + " AS " + Presence.PRESENCE_CUSTOM_STATUS);
697a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        columns.put(Presence.PRESENCE_CUSTOM_STATUS_TIMESTAMP,
698a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                StatusUpdatesColumns.TIMESTAMP + " AS " + Presence.PRESENCE_CUSTOM_STATUS_TIMESTAMP);
699373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        sPresenceProjectionMap = columns;
700373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov
701a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap = new HashMap<String, String>();
702a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap.putAll(sDataProjectionMap);
703a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap.put(Presence.PRESENCE_STATUS,
704e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                Presence.PRESENCE_STATUS);
705a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap.put(Presence.PRESENCE_CUSTOM_STATUS,
706a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                StatusUpdatesColumns.STATUS + " AS " + Presence.PRESENCE_CUSTOM_STATUS);
707a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        sDataWithPresenceProjectionMap.put(Presence.PRESENCE_CUSTOM_STATUS_TIMESTAMP,
708a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                StatusUpdatesColumns.TIMESTAMP + " AS " + Presence.PRESENCE_CUSTOM_STATUS_TIMESTAMP);
70919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
7101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
7111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
7121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
7131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
7141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
7151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
7161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
7171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
7181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
7191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
7201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
7211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
7224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsInGroupSelect = Contacts._ID + " IN "
7234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                + "(SELECT " + RawContacts.CONTACT_ID
7244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                + " FROM " + Tables.RAW_CONTACTS
7254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
7264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
7274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        + " FROM " + Tables.DATA_JOIN_MIMETYPES
7284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
7294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + "' AND " + GroupMembership.GROUP_ROW_ID + "="
7304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + "(SELECT " + Tables.GROUPS + "." + Groups._ID
7314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + " FROM " + Tables.GROUPS
7324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + " WHERE " + Groups.TITLE + "=?)))";
7334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
7344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
7353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
7363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
7373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
7383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
7393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
741653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
7423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
7443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
745a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
746a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
747a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
748a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
749a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
750a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
751a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
752a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
7533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
755653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
756653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
757653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                mMimetypeId = mOpenHelper.getMimeTypeId(mMimetype);
758653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
759653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
760653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
761653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
7623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
7633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
7643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
7655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
766e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
767e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
768e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
769e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
770653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
771e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
772e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
773e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
7743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
7773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
7783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
7793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
780653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
781f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
78214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
78314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
784653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
785653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
786653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
787653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
788653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
789653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
790653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
791653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
792653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
793653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
794653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
795653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
796653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
797653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
798653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
799653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
800653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
801653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " = " + dataId, null);
802653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
803653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
804f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
805653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
806653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
8073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
81014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
81114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
81214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
8133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
8143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
8155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
8163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
8183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
8215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, rawContactId);
8223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
82314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                setIsPrimary(rawContactId, newPrimaryId, getMimeTypeId());
8243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long rawContactId) {
828e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
829e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
8305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Cursor c = queryData(db, rawContactId);
8313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
832e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
83314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
834f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
835e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
836e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
837e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
838e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
8393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
8403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
8413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
8423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
843e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
844e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
845e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
846e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
847e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
848e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
849e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
850e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
851e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
8523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long rawContactId) {
85514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return db.query(DataDeleteQuery.TABLE, DataDeleteQuery.CONCRETE_COLUMNS,
85614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId +
85714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
8583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
8593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
86125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
8623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String bestDisplayName = null;
86325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
86425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
86567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Cursor c = db.query(DisplayNameQuery.TABLE, DisplayNameQuery.COLUMNS,
8665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId, null, null, null, null);
8673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
8683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                while (c.moveToNext()) {
8693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String mimeType = c.getString(DisplayNameQuery.MIMETYPE);
870a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
871a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    // Display name is at DATA1 in all type.  This is ensured in the constructor.
872a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    String name = c.getString(DisplayNameQuery.DATA);
873a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    if (TextUtils.isEmpty(name)
874a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                            && Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
875a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        name = c.getString(DisplayNameQuery.TITLE);
8763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
877a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    boolean primary = StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
878a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        || (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
8793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
88001911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                    if (name != null) {
88125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        Integer source = sDisplayNameSources.get(mimeType);
88201911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                        if (source != null
88301911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                && (source > bestDisplayNameSource
88401911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                        || (source == bestDisplayNameSource && primary))) {
88525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                            bestDisplayNameSource = source;
8863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            bestDisplayName = name;
8873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        }
8883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
8893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
8903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
8923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
8933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
89525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
896285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
897285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateDisplayName(db, rawContactId);
898285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
8993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
900a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
901a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
902a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return true;
903a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
904622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
905622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
906622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
907622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
908622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
909622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
910622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
911622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
912622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=" + dataId,
913622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    null, null, null, null);
914622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
915622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
916622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
917622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
918622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        values.put(key, cursor.getString(i));
919622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
920622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
921622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
922622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
923622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
924622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
925622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
926622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
9273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
9283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
9303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
9323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
9333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
9353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
937622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
9383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
939622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
9403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
941622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
9423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
9455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
946622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
94714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
94814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
94914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
950f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
951f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
95225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
95314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
95414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
95514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
95614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
95714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
958f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
959622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
960622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
961cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
962622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
963622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
96414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
965f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
96614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
967f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
968f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
969f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
970f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
97114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
97225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
97314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
97414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
97514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
97614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
97714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
97814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
97914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
98014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
98114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
982f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
98325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
98414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
9853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
988622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
9893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
990622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
991622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
992622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
993622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
9943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
995622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
996622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
997622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
998622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
999622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1000622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1001622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
1002622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1003622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1004622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
1005622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1006622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1007622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
10088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1009622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1010622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
1011622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } else if (!touchedUnstruct && touchedStruct) {
10128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1013622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
1014622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(name);
1015622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
1016622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1017622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1018622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1019622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1020622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1021622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1022622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1023622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1024622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1025622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1026622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1027622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1028622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1029622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1030622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1031622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1032622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1033622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1034622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1035622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1036f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1037622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1038622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1039622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1040f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1041622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1042622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1043622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1044622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1045622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1046622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1047622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1048622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1049622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1050622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1051622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1052622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1053622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1054622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1055622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1056622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1058622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
1059622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
1060622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1061622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
1062622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1063622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1064622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1065622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1066622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1067622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1068622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
1069622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } else if (!touchedUnstruct && touchedStruct) {
1070622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1071622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1072622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
10733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
10783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
10803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
10813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
10833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
10853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
10863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1090622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1091622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1092622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1094622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1095622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1096f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1097622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1098622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1099622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1100f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1101622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1103622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1104622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1105622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1106622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1107622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1108622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1109622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1110622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
11113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1112622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1113622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1114622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1116622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
11173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
11213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
11233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
11243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1128a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1129a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1130a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1131a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1132a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
113325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1134a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1135a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
11363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
113914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1140f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1141a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1142a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1143a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
114414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
114514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1146f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
114714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
114825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1149a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
1150a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
115114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
115214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
115414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1155a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
115614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
115714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
115925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1160a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
116114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
116214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
116314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
116414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
11653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
11663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
11673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
11683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
11693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
11703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
11713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
11723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1173a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1174a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1175a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1176a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1177a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
11783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1180e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1181e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1182e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1183e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1184e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1185e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1186e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
11875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
118814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
118914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
119014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
119114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
119225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1193f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
119414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
119514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
119614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
119714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
119814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1199f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
120014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
120114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
120214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
120314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1204f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
120514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1206f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1207f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
120825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
120914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
121014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
121114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
121214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
121314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
121414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
121514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
121614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
121714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1218f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
121925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
122014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1221e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1222e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1223e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1224e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1225e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1226e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1227e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1228e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1229e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1230e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1231e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1232e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1233e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1234e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
123514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
123614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
123714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
123814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
123914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
124014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
124214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
124314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
124414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
124614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1248f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
124914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
125014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
125114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
125214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
125314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1254f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
125514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
125614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
125714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
125814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1259f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
126014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1261f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1262f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
126325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
126414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
126514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
126614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
126714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
126814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
126914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
127014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
127114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
127214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1273f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
127425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
127514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
127614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
127714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
127814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
12793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
12803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
12823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
12833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
12870b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
12880b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
12890b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
12900b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1291653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
12920b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1293653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
12940b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1295285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
129625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
12970b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
12980b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
12990b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1300653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1301653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1302653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1303653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1304653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1305f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
130614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
130714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
13080b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
13090b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
13100b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1311653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1312f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
1313653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13140b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1315285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
131625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
13170b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1318f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
13190b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
132014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
132114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
132314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
132414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
132514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
132614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
132814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1330285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
133125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
133214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1333653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1334653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1335653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1336e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1337e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1338e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1339e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1340653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1341653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1342653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1343e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1344653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1345653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1346e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1347653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
13485ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1349653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1350e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1351653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1352653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
1353653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=" + dataId, null);
1354e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
13553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
13593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
13603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
13613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
13623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
13633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
13643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
13653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
13663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
13673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
13683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
13693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
13703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1373653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1374653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1375653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1376653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1377653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1378653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1379653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1380653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1381653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
13820be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
13830be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
13840be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1385653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1386653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1387653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1388653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1389f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
139014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1391653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1392f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
13930be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
13940be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
13950be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
13960be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
13970be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
13980be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
13990be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
14000be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
14010be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
14020be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
14030be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14040be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
14050be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long contactId = mOpenHelper.getContactId(rawContactId);
14060be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
14070be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov                mOpenHelper.updateContactVisible(contactId);
14080be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1409653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1410653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1411653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1412653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1413653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1414653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1415653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1416653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1417653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1418653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1419653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1420653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1421653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1422653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1423653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1424653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1425653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1426653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1427653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1428653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1429653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1430653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1431653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1432653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1433653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId);
1434653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1435653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1436653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1437653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1438a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1439a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1440a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1441a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1442a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1443653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1444653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1445a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1446a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1447a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1448a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1449a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1450a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1451a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1452a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1453a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1454285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1455285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1456285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1457a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1458a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1459a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1460a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1461a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1462f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1463a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1464f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1465a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1466a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1467a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1468a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1469a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1470a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1471a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1472a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1473a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1474a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1475a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1476a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1477a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1478a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1479a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1480a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1481a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1482a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
14833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
148453056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
14854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
148631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
14874097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1488f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1489f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache =
1490f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            new HashMap<String, SoftReference<String[]>>();
1491622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1492622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1493622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1494f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1495a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1496a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
149720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
149820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1499ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
150073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private boolean mImportMode;
150173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1502b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mInsertedRawContacts = Sets.newHashSet();
1503b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1504b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1505de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
150681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
150781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
1508a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
150953056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
1510a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1511a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1512a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1513a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
1514a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
151553056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
151653056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
1517a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
15184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
15204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1521de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
152235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1523de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1524de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mOpenHelper = (OpenHelper)getOpenHelper();
1525a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1526a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mOpenHelper, this, mGlobalSearchSupport);
1527cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mOpenHelper, mAggregationScheduler);
15280e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1529a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1530d51a83ac4f8032b62d9a23b90a8f43d6b7eb2dbbDmitri Plotnikov        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1531653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1532c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1533653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1534653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1535653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1536653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1537653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1538c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1539653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1540653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1541653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1542653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1543653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1544653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1545653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1546653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1547653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1548653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1549653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
15505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
15516cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                + RawContacts.TIMES_CONTACTED + "=" + RawContacts.TIMES_CONTACTED + "+1,"
1552d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + RawContacts.LAST_TIME_CONTACTED + "=? WHERE " + RawContacts.CONTACT_ID + "=?");
1553a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
155425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate = db.compileStatement(
155525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
155625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
155725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
155825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
15593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
156073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
156173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                + RawContacts.DIRTY + "=1 WHERE " + RawContacts._ID + "=?");
156273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1563a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mLastStatusUpdate = db.compileStatement(
1564a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.CONTACTS
1565a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                + " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1566a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1567a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1568a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1569a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1570a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1571a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1572a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1573a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1574a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
1575a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " ORDER BY " + StatusUpdatesColumns.TIMESTAMP + " DESC" +
1576a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " LIMIT 1)"
1577a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                + " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1578e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1579622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        final Locale locale = Locale.getDefault();
158028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
158128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
158228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
158328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
1584622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1585622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                locale);
1586f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1587622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
15884097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1589f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1590f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1591f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1592f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1593f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1594f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1595f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1596a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateInsert = db.compileStatement(
1597a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1598a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
1599a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.TIMESTAMP + ","
1600a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.STATUS + ")" +
1601a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " VALUES (?,?,?)");
1602a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1603a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateReplace = db.compileStatement(
1604a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1605a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
1606a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.TIMESTAMP + ","
1607a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.STATUS + ")" +
1608a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " VALUES (?,?,?)");
1609a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1610a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateAutoTimestamp = db.compileStatement(
1611a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
1612a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " SET " + StatusUpdatesColumns.TIMESTAMP + "=?,"
1613a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.STATUS + "=?" +
1614a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
1615a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + " AND " + StatusUpdatesColumns.STATUS + "!=?");
1616a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1617a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateDelete = db.compileStatement(
1618a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1619a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1620a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
16213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
16223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1623e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
16243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
16253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
162667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
162767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
16283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
16293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
163014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
16313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
16323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1633622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1634622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1635a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1636a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
16373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1639568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            importLegacyContactsAsync();
16403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1641568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1642c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        verifyAccounts();
164370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
16441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
16454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
16464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1647c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
1648c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1649c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1650c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
1651c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
165231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1653de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
165431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
165531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
165631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
165731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1658285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    /* package */ ContactAggregationScheduler getContactAggregationScheduler() {
1659285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mAggregationScheduler;
1660285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1661285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1662013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1663013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1664013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1665013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
16663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
16673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
16683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
16693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
16703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1671568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1672568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1673568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1674568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1675568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1676568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
1677568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
1678568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1679568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
1680ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        mAccessLatch = new CountDownLatch(1);
1681568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1682568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
1683568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
1684568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
1685568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                if (importLegacyContacts()) {
1686568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1687568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    /*
1688568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * When the import process is done, we can unlock the provider and
1689568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * start aggregating the imported contacts asynchronously.
1690568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     */
1691ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch.countDown();
1692ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1693568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    scheduleContactAggregation();
1694568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
1695568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1696568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
1697568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1698568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
1699568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1700568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
17013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
1702568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1703568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        if (importLegacyContacts(importer)) {
17043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
17053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
17063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
17073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
17083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
17093d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
17103d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
17113d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
17123d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17133d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
17143d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1715568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
17160e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
17173d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
171873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mImportMode = true;
17193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
17203d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
17210e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff            mContactAggregator.setEnabled(aggregatorEnabled);
17223d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
17233d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
17243d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
17253d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
172673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } finally {
172773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mImportMode = false;
17283d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
17293d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17303d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1731a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
1732a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
1733a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
1734a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
1735a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
1736a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1737a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
1738a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1739a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1740a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1741a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1742a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1743a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1744a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
1745a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1746a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1747568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1748568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1749568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1750568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1751568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1752568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1753568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1754568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
1755ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
1756ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
1757ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
1758ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
1759ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
1760ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1761ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
1762ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
176381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
1764ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
1765ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1766568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1767568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1768568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1769568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1770568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1771568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1772568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1773568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1774568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1775568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1776568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1777568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1778568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1779568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1780568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1781568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1782568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
1783568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1784568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1785568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1786568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1787568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1788568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1789568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
1790568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1791568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1792568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1793568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
17944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1795285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1796bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1797b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1798b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1799285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
18001ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1801b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1802b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1803b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1804b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
1805285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
1806b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
1807df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
1808285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1809285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1810285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1811285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
1812bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1813b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1814b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1815285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1816b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
18171ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
1818b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1819b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1820b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1821bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1822b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1823b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1824b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long rawContactId : mInsertedRawContacts) {
1825b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mContactAggregator.insertContact(mDb, rawContactId);
1826285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1827b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1828b5a4add17815167d20a90645779df34cdf45280dFred Quintana        String ids;
1829b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
1830b5a4add17815167d20a90645779df34cdf45280dFred Quintana            ids = buildIdsString(mUpdatedRawContacts);
1831b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mDb.execSQL("UPDATE raw_contacts SET version = version + 1 WHERE _id in " + ids,
1832b5a4add17815167d20a90645779df34cdf45280dFred Quintana                    new Object[]{});
1833b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1834b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1835b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
1836b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
1837b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mOpenHelper.getSyncState().update(mDb, id, entry.getValue());
1838b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1839b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1840b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1841b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1842b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1843b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private String buildIdsString(HashSet<Long> ids) {
1844b5a4add17815167d20a90645779df34cdf45280dFred Quintana        StringBuilder idsBuilder = null;
1845b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1846b5a4add17815167d20a90645779df34cdf45280dFred Quintana            if (idsBuilder == null) {
1847b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder = new StringBuilder();
1848b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append("(");
1849b5a4add17815167d20a90645779df34cdf45280dFred Quintana            } else {
1850b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append(",");
1851b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
1852b5a4add17815167d20a90645779df34cdf45280dFred Quintana            idsBuilder.append(id);
1853b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1854b5a4add17815167d20a90645779df34cdf45280dFred Quintana        idsBuilder.append(")");
1855b5a4add17815167d20a90645779df34cdf45280dFred Quintana        return idsBuilder.toString();
1856285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1857285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1858285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1859cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
186081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
186181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
186281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
186381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
186481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
186581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
186681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1867cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1868568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1869568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected void scheduleContactAggregation() {
1870dee54bb86f3608730f0b9f37d8982a7f6b280a85Dmitri Plotnikov        mContactAggregator.schedule();
1871568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1872568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1873285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
1874285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mInsertedRawContacts.contains(rawContactId);
1875285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1876285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
18773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
18783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
18793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
18803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
18813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
18823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
18833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
18843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
18853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
18864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1887de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
1888bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1889b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "insertInTransaction: " + uri);
1890b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1891f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1892f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
1893f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
1894f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1895a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1896a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
189735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1898a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
189935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
1900de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                id = mOpenHelper.getSyncState().insert(mDb, values);
190135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
190235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1903d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1904d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
19056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
19066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
19076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
19085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
1909f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
1910d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                id = insertRawContact(values, account);
1911f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1912a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1913a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1914a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
19155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
19165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1917f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1918f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1919a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1920a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1921a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1922a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1923f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1924f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1925a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1926a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1927a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1928ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1929ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
19305aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertGroup(uri, values, account, callerIsSyncAdapter);
1931f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1932ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1933ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1934ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1935eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
19365aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
193743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
1938eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
1939eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
1940eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
19411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
19421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
19431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
19441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
19451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1946a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
194781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
1948f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1949a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1950a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
19517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
19527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
19537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
19547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1955de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
1956a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1957a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1958a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1959035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
1960035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
1961035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
1962035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
1963035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
19647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1965035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
1966035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
19676cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
19686cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1969035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
1970035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
1971035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
1972035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
1973035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
1974035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
1975035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1976035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
1977df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_NAME, account.name);
1978df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_TYPE, account.type);
1979035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1980035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
19817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
19827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1984d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
19856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
19866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
19876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
19886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
1989d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
1990de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
19916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
19926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
19936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
1994a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
1995a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1996a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1997f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
1998a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1999a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2000d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertRawContact(ContentValues values, Account account) {
2001a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
2002d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        overriddenValues.putNull(RawContacts.CONTACT_ID);
2003f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
20047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
20057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
20067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
20073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
20083d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
20093d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            overriddenValues.put(RawContacts.AGGREGATION_MODE,
20103d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                    RawContacts.AGGREGATION_MODE_DISABLED);
20113d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
20123d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2013023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        long rawContactId =
2014023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov                mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, overriddenValues);
2015023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
2016285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2017285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2018285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.add(rawContactId);
2019023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2020a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2021a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2022a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2023a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2024a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2025a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2026a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2027a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2028f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2029a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2030de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2031de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
203267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2033de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
203420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2035de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2036de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2037de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2038de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
2039de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2040de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2041508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2042de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2043de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2044de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2045de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2046de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
20474097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2048de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
2049de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2050a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2051a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2052a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2053f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2054de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2055a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2056b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2057a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2058a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2059a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2060a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2061a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
20624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
20634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
20648e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
20658e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
20668e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
20678e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
20688e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
20698e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        int aggregationMode = mOpenHelper.getAggregationMode(rawContactId);
2070f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
20718e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
20728e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
20738e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
20748e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2075421782cb554e5050cf62a86b98df6520038dcd15Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId);
2076f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
20778e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
20788e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
20798e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
20808e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                long contactId = mOpenHelper.getContactId(rawContactId);
2081f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
20828e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
20838e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
20848e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2085f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
20868e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2087f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2088c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
20898e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                long contactId = mOpenHelper.getContactId(rawContactId);
20908e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2091f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
20928e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2093f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2094f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2095f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2096a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
20975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
20989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
20999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
21005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
21019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
21029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
21039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
21049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
21059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
21065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId) {
21079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
21086cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, RawContacts._ID + "="
21095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                + rawContactId, null, null, null, null);
21109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
21119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
211267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountName = c.getString(ContactsQuery.ACCOUNT_NAME);
211367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountType = c.getString(ContactsQuery.ACCOUNT_TYPE);
21149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
21159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
21169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
21179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
21189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
21199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
21209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
21219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
21229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
21239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
21249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
21259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
21269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
21279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
21285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
21296cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
21309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2131df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
21329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
21339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
21349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
21359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
21369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2137df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2138df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
21399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
21409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
21419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
21429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
21439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
21449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
21459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
21469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
21479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
21489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
21499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
21509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
21519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
21529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
215320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
215420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2155f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
215620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
215720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2158de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2159de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
216014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2161de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2162de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
216314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
216414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2165a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2166a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2167f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
216888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
2169a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    if (rowHandler.isAggregationRequired()) {
2170a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        triggerAggregation(rawContactId);
2171a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    }
217288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
217320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
217420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2175de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
217620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
217720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
217820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
217920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
218020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
218188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
218288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
218388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
218420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2185f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
218688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
218788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
218814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=" + dataId, null,
218914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                null);
2190f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
219120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
219220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
219320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
219420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
219520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
219614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
219720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
219820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
219920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
220020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
220120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
220220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
220320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
220420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
220520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
22067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
220720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
220820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
220920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2210a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2211a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = rowHandler.delete(mDb, c);
22128e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2213a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            if (rowHandler.isAggregationRequired()) {
2214a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                triggerAggregation(rawContactId);
2215a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            }
22168e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return count;
221720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
221820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
221920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
222020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
222120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
222220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
22235aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     * Determine if the given {@link Uri} and {@link ContentValues} should
22245aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     * trigger a call to {@link OpenHelper#updateAllVisible()}, usually based on
22255aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     * query parameters like {@link Contacts#DELAY_STARRED_UPDATE} and columns
22265aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     * like {@link Groups#GROUP_VISIBLE}.
22275aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     */
22285aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private boolean shouldUpdateAllVisible(Uri uri, ContentValues values, String visibleColumn) {
22295aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        final boolean delayUpdate = readBooleanQueryParameter(uri,
22305aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                Contacts.DELAY_STARRED_UPDATE, false);
22315aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        final boolean forceUpdate = readBooleanQueryParameter(uri,
22325aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                Contacts.FORCE_STARRED_UPDATE, false);
22335aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        final boolean touchedVisible = (values == null) ? true : values.containsKey(visibleColumn);
22345aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
22355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        return forceUpdate || (!delayUpdate && touchedVisible);
22365aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    }
22375aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
22385aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    /**
22395aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     * Determine if the given {@link Uri} and {@link ContentValues} should
22405aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     * trigger a call to {@link OpenHelper#updateAllVisible()}, usually based on
22415aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     * query parameters like {@link Contacts#DELAY_STARRED_UPDATE}.
22425aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey     */
22435aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private boolean shouldUpdateAllVisible(Uri uri) {
22445aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        return shouldUpdateAllVisible(uri, null, null);
22455aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    }
22465aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
22475aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    /**
2248ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2249ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
22505aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertGroup(Uri uri, ContentValues values, Account account, boolean callerIsSyncAdapter) {
2251ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
2252ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
2253ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
2254ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2255ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2256ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
225767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE);
225867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
225967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            overriddenValues.put(GroupsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
226067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
226167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        overriddenValues.remove(Groups.RES_PACKAGE);
2262ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2263f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
226473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            overriddenValues.put(Groups.DIRTY, 1);
226573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
226673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2267ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
2268ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
22695aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        if (shouldUpdateAllVisible(uri, overriddenValues, Groups.GROUP_VISIBLE)) {
2270ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey            mOpenHelper.updateAllVisible();
2271ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2272ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2273ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2274ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2275ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
22765aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2277e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
22785aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
22795aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        if (shouldUpdateAllVisible(uri, values, Settings.UNGROUPED_VISIBLE)) {
2280e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            mOpenHelper.updateAllVisible();
2281e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2282e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2283e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2284e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2285ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
22861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
22871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
228870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public long insertPresence(ContentValues values) {
22891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
22904dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        if (TextUtils.isEmpty(handle) || !values.containsKey(Presence.PROTOCOL)) {
22914dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
22924dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        }
22934dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
22944dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        final long protocol = values.getAsLong(Presence.PROTOCOL);
22954dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
22964dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
22974dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        if (protocol == Im.PROTOCOL_CUSTOM) {
22984dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            customProtocol = values.getAsString(Presence.CUSTOM_PROTOCOL);
22994dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
23004dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
23014dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
23024dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
23031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
23041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
23051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
23064dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
23071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
230870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        StringBuilder selection = new StringBuilder();
23091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
23101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
23114dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selection.append(
23124dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    "((" + MimetypesColumns.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
23134dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.PROTOCOL + "=?"
23144dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.DATA + "=?");
23154dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (customProtocol != null) {
23164dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                selection.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
23174dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(selection, customProtocol);
23184dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
23194dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selection.append(") OR ("
23204dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + MimetypesColumns.MIMETYPE + "='" + Email.CONTENT_ITEM_TYPE + "'"
23214dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Email.DATA + "=?"
23224dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + "))");
23234dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selectionArgs = new String[] { String.valueOf(protocol), handle, handle };
23241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
23254dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selection.append(
23264dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    MimetypesColumns.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
23274dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.PROTOCOL + "=?"
23284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.DATA + "=?");
23294dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (customProtocol != null) {
23304dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                selection.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
23314dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(selection, customProtocol);
23324dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
23334dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
23344dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selectionArgs = new String[] { String.valueOf(protocol), handle };
23351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
23361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
233770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (values.containsKey(Presence.DATA_ID)) {
233870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append(" AND " + DataColumns.CONCRETE_ID + "=")
233970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    .append(values.getAsLong(Presence.DATA_ID));
234070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
234170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
234200ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        selection.append(" AND ").append(getContactsRestrictions());
234370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
23441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
23455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        long rawContactId = -1;
2346e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        long contactId = -1;
234770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
23481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
23491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2350de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
235170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    selection.toString(), selectionArgs, null, null, null);
23521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
235367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
23545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2355e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
23561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
23571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
23581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
23591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
23601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
236131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
236231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
236331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
23641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
23651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2366a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (values.containsKey(Presence.PRESENCE_STATUS)) {
2367a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2368a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2369a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2370a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2371a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2372a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2373a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
2374a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // TODO deprecate and then remove the Presence._ID column
2375a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(Presence._ID, dataId);
2376a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(Presence.DATA_ID, dataId);
2377a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2378a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
2379a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(Presence.PROTOCOL, protocol);
2380a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(Presence.CUSTOM_PROTOCOL, customProtocol);
2381a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(Presence.IM_HANDLE, handle);
2382a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (values.containsKey(Presence.IM_ACCOUNT)) {
2383a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mValues.put(Presence.IM_ACCOUNT, values.getAsString(Presence.IM_ACCOUNT));
2384a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2385a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(Presence.PRESENCE_STATUS, values.getAsString(Presence.PRESENCE_STATUS));
23861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2387a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2388a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2389a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2390e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
2391a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (values.containsKey(Presence.PRESENCE_CUSTOM_STATUS)) {
2392e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            String status = values.getAsString(Presence.PRESENCE_CUSTOM_STATUS);
2393a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
2394a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
2395a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
2396a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else if (values.containsKey(Presence.PRESENCE_CUSTOM_STATUS_TIMESTAMP)) {
2397a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                long timestamp = values.getAsLong(Presence.PRESENCE_CUSTOM_STATUS_TIMESTAMP);
2398a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
2399a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
2400a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindString(3, status);
2401a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
2402a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
2403a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                long timestamp = System.currentTimeMillis();
2404a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2405a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
2406a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
2407a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(2, timestamp);
2408a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindString(3, status);
2409a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
2410a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
2411a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
2412a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
2413a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindString(2, status);
2414a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
2415a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindString(4, status);
2416a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
2417a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
2418e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2419e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2420bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2421a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2422a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
2423a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
2424a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
2425a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2426a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2427a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
24281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
24291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
24304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2431de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2432bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2433b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2434b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2435b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2436f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2437f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2438508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2439508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
244035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2441de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mOpenHelper.getSyncState().delete(mDb, selection, selectionArgs);
244235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2443b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2444b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2445b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2446b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2447b5a4add17815167d20a90645779df34cdf45280dFred Quintana                return mOpenHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2448b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2449cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2450cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2451cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2452cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2453cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2454d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2455d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2456cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
24576bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24586bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
24602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
24612e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
24622e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
24632e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
24642e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
24652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
24662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
24672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
24682e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
24692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
24702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
24712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
24722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
24732971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2474e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
24752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
24762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
24772971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2478f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        numDeletes += deleteRawContact(rawContactId, callerIsSyncAdapter);
24792971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
24802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
24812971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
24822971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
24832971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
24842971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
24852971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
24865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
24872971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2488f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteRawContact(rawContactId, callerIsSyncAdapter);
2489508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2490508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
249120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2492f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2493944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2494f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
249520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
249620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
249748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
249848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
249948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
250048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2501508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2502f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2503f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteData(Data._ID + "=" + dataId, null, callerIsSyncAdapter);
2504ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2505ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2506ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2507f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
25085aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
25092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
25102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
25112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
25122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
25132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2514e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
25152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
25162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
25175aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
25182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
25192971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
25202971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
25212971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
252281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2523f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
252481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
25252971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2526508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2527508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2528eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
252943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
25305aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteSettings(uri, selection, selectionArgs);
2531eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2532eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
25331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
2534eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
25351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
25361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
253781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
253881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
25393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
254081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2541508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
25424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
25434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
25442971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana    private boolean readBooleanQueryParameter(Uri uri, String name, boolean defaultValue) {
25452971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        final String flag = uri.getQueryParameter(name);
25462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        return flag == null
25472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                ? defaultValue
25482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                : (!"false".equals(flag.toLowerCase()) && !"0".equals(flag.toLowerCase()));
254994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
255094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
25515aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
255294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        final long groupMembershipMimetypeId = mOpenHelper
255394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2554de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
255594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
255694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
255794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
255894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2559f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2560de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
256194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
256294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
256394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2564f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2565de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
256694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
256794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
25685aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey            if (shouldUpdateAllVisible(uri)) {
25695aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                mOpenHelper.updateAllVisible();
25705aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey            }
257194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
257294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
257394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
25745aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2575e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
25765aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        if (count > 0 && shouldUpdateAllVisible(uri)) {
2577e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            mOpenHelper.updateAllVisible();
2578e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2579e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2580e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2581e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2582cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
2583cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2584cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
2585cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2586cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2587cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2588cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
2589cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2590cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2591cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2592cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2593cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2594cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2595cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2596cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2597f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    public int deleteRawContact(long rawContactId, boolean callerIsSyncAdapter) {
2598f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
259914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2600de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
260133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2602a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov            mOpenHelper.removeContactIfSingleton(rawContactId);
2603cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
260433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
260533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
260633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
2607cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
260881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
260981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2610cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2611cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2612cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2613cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2614cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2615cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2616cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
2617cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2618cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
26194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2620de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2621de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2622bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2623b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2624b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2625b5a4add17815167d20a90645779df34cdf45280dFred Quintana
262635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
262700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
262800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2629b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2630b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
2631b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Object data = values.get(ContactsContract.SyncStateColumns.DATA);
2632b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
2633b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2634b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2635b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2636f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2637f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
263800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
263935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2640b5a4add17815167d20a90645779df34cdf45280dFred Quintana                return mOpenHelper.getSyncState().update(mDb, values,
2641b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2642b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2643b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2644b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2645b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2646b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2647b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2648b5a4add17815167d20a90645779df34cdf45280dFred Quintana                return mOpenHelper.getSyncState().update(mDb, values,
2649b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2650b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
265135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2652d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
26538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
265400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
265500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
265600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2657d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
26588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
2659c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2660c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2661c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
26622e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
26632e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
26642e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
26652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
26662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
26672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
26682e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
26692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
26702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
26718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
26722e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
26732e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
26742e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
26757d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
26767d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
26777d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
26787d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
26797d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
26807d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
26817d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
26827d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
26837d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
26847d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
268520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2686944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2687f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
268881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2689f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
269081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
269120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
269220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2693c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
269448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
269548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
269648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
269748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2698f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
269981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2700f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
270181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
270200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
270300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
27047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
27065ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
27074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
27087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
27097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
27107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
271233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
27134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
27144529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId
27154529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
27164529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
27174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    count = updateRawContact(rawContactId, values);
27184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
27197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
27207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
27217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2722ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
27235aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2724f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
272581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2726f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
272781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2728ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2729ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2730ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2731ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2732ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
273373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                String selectionWithId = (Groups._ID + "=" + groupId + " ")
273473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
27355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
27365aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
273781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2738f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
273981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2741ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2742ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2743127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2744de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2745b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2746b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2747b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2748eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
27495aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateSettings(uri, values, selection, selectionArgs);
275043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2751eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2752eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2753eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
275481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
275581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2756f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
275781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
275800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
275900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
276000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
27614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
27635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
2764f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
276573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
276673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2767f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
276873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
276973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
277073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
277173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
277273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
277373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
277473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
277573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2776ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
277794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
277894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        // If changing visibility, then update contacts
27795aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        if (shouldUpdateAllVisible(uri, updatedValues, Groups.GROUP_VISIBLE)) {
278094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            mOpenHelper.updateAllVisible();
278194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
278294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
278394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
278494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
27855aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateSettings(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2786e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
27875aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey        if (shouldUpdateAllVisible(uri, values, Settings.UNGROUPED_VISIBLE)) {
2788e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            mOpenHelper.updateAllVisible();
2789e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2790e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2791e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2792e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
27934529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
27944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
27954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
27964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
27974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
279873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
27994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
28004529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        Cursor cursor = mDb.query(mOpenHelper.getRawContactView(),
280151bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
28024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
28034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
28044529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
28054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
28064529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
28074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
28084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
28094529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
28104529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
28114529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
28124529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
28134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
28144529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
28154529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
28164529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
28174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = mDb.update(Tables.RAW_CONTACTS, values, RawContacts._ID + " = " + rawContactId,
28184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                null);
28195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
2820433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
28214529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
2822433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
2823285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
2824285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateLookupKey(mDb, rawContactId);
2825285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
28265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
28275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
282833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
282933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
2830321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
2831f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
283220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
283320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
283420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
28355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
283620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
283720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
283820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
283920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
284020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
284120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
284220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
284320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
284470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
284570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
284620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
284720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
284820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
284970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
285020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
285170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
285220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
285370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
285420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
285570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
285620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
285720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2858653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
285920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2860653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2861653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
286214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
2863653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
2864653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
2865f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
286620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2867653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
2868653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
286920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
287020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2871653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
287220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
287320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2874f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
2875653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
2876653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
2877321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
2878653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
287914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
2880a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2881f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
28828e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
2883a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2884a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2885a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
28868e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2887653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return 1;
2888321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
2889321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
28908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
28918c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
28928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
28938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        Cursor cursor = mDb.query(mOpenHelper.getContactView(),
28948c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
28958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
28968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
28978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
28988c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
28998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
29008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
29018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
29028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
29038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
29048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
29058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
29068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
29078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
29088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
29098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
2910d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
29118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
29128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
2913d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
29148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
2915d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
29168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
2917d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
29188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
2919d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
29208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.STARRED,
2921d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
2922d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2923d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
29248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
2925d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
2926d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2927d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
29288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
2929c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
29308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
2931c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
2932c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
29338c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=" + contactId, null);
29348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
29358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
29368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
29378c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
29388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
29398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
29408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
29418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
29428c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
29438c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
29448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
29458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
29468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.STARRED,
29478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
29488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
29498c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=" + contactId, null);
2950f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2951d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2952d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    public void updateContactTime(long contactId, long lastTimeContacted) {
2953f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
2954d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(2, contactId);
2955f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.execute();
2956d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
2957d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2958127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
2959127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
29600c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
29610c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
296280c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
29630c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
29640c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
29650c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
29660c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
29670c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
29680c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
29690c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
2970b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
2971127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
29720c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
29730c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
29740c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId1 + " AND "
29750c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId2, null);
29760c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
29776bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
29786bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
29790c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
29800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
29810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
29820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
2983127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
2984127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2985dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
2986dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
2987dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
29880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long contactId1 = mOpenHelper.getContactId(rawContactId1);
29890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
29900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
29910c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long contactId2 = mOpenHelper.getContactId(rawContactId2);
29920c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
2993127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2994127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
2995127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
2996127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
2997b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
2998b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
299970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
300070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb = mOpenHelper.getWritableDatabase();
300170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        if (mDb == null) return;
300270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
300370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        Set<Account> validAccounts = Sets.newHashSet();
300470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        for (Account account : accounts) {
300570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            validAccounts.add(new Account(account.name, account.type));
300670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
300770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        ArrayList<Account> accountsToDelete = new ArrayList<Account>();
300870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
300970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
301070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
301148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
30125f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana            for (String table : new String[]{Tables.RAW_CONTACTS, Tables.GROUPS, Tables.SETTINGS}) {
30135f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                // Find all the accounts the contacts DB knows about, mark the ones that aren't
30145f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                // in the valid set for deletion.
30155f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Cursor c = mDb.rawQuery("SELECT DISTINCT account_name, account_type from "
30165f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        + table, null);
30175f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                while (c.moveToNext()) {
30185f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                    if (c.getString(0) != null && c.getString(1) != null) {
30195f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        Account currAccount = new Account(c.getString(0), c.getString(1));
30205f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        if (!validAccounts.contains(currAccount)) {
30215f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                            accountsToDelete.add(currAccount);
30225f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        }
302370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                    }
302470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                }
30255f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                c.close();
302670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
302770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
302870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
30295f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
303070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                String[] params = new String[]{account.name, account.type};
303170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.GROUPS
303270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?", params);
303370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.PRESENCE
303470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (SELECT "
303570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + RawContacts._ID + " FROM " + Tables.RAW_CONTACTS
303670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?)", params);
303770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.RAW_CONTACTS
303870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?", params);
30395f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                mDb.execSQL("DELETE FROM " + Tables.SETTINGS
30405f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        + " WHERE account_name = ? AND account_type = ?", params);
304170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
30424637c20c6b6d3b6f6671e6a44ed57f1e5b9c4484Dmitri Plotnikov            mOpenHelper.getSyncState().onAccountsChanged(mDb, accounts);
304370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
304470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
304570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
304670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
304770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3048619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3049619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3050622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
3051622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
3052622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private static boolean areAllEmpty(ContentValues values, String[] keys) {
3053622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
3054622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (!TextUtils.isEmpty(values.getAsString(key))) {
3055622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                return false;
3056622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
3057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
3058622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        return true;
3059622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
3060622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
30614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
30624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
30634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
3064bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3065bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3066bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
30670b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
30684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
306935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3070d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
30711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3072c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3073c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3074619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3075619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3076a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
30774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
307835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
307935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
308035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
308135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3082d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3083ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
3084619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3085619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3086619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3087d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
30884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3089ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
30904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + contactId);
30916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
30926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
30936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
30945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
30955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
30965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
30975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
30985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
30995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
31005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
31015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
31025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
31035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
31045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
31055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    setTablesAndProjectionMapForContacts(lookupQb, projection);
31065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=" + contactId + " AND " +
31075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            Contacts.LOOKUP_KEY + "=");
31085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhereEscapeString(lookupKey);
31095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, selectionArgs, sortOrder,
31105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
31115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
31125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
31135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
31145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
31155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
31165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
31175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
31185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
31195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
31205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
31215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
31225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3123ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3124ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
3125ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
31264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
31274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3128e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
31295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
31304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sb.toString());
3131ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3132ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3133ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3134ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3135ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3136ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
31374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3138ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3139d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
31404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
31414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3142e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
31435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
31444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
31454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
31464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3147ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
3148ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
31494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
31504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
31514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3152d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3153d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                final String starredQuery = qb.buildQuery(projection, Contacts.STARRED + "=1",
31544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3155d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3156d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3157d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3158ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
31594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
31604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3161d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3162d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
3163d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3164d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
31654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3166d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3167d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3168d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3169d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
31704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
31714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3172d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3173d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3174d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3175d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3176d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3177d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3178ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3179ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
3180b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
31814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sContactsInGroupSelect);
31824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3183b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3184b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3185b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3186b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3187d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
31884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
31894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
31904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
31914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
31924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
31934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
31946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
31956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
319600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3197ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
31983653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
31993653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
32003653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32013653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
32023653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
32033653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
32043653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
32053653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
32063653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
32073653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
32084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
32094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
321189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
321289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
32132815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
32142815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
32152815f58f72f109790585931f601a63ddc02536a5Evan Millar
321648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
321748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
321848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
321948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
322048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
322148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
322248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
322348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
322448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3225ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
32264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                qb.setProjectionMap(sDistinctDataProjectionMap);
322889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
322989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3230ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
32314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
32324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
32335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
32345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
32355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
32365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
32375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
32385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
32395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null);
32405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
32415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
32425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
32435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
32445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
32455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
32465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
32475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
32485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
32495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
32505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
32515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
32525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
32535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
32545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
32555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
32565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
32574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
3258ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
32595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3260ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3261ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3262ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
32634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
32644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
326689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
326789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
32684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
32694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
32704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
327148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
327248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
327348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
327448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
327548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
327648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
327748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
327848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
327948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
32805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
32814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
328389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
328489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
32854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
32865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + Email.DATA + "=");
32874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhereEscapeString(uri.getLastPathSegment());
32884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3289ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3290ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3291ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
32925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
32935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                qb.setProjectionMap(sDistinctDataProjectionMap);
329589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
329689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
32975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
32985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
32995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
33005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
33015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
33025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
33035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
33045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
33055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null);
33065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(" OR ");
33075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
33085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
33095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(Email.DATA + " LIKE ");
33101e530df9f7e496dc47f77d4323c89bd413b79b64Dmitri Plotnikov                    sb.append(DatabaseUtils.sqlEscapeString(filterParam + '%'));
33115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
33125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
33135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
33145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
33155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
33165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
33175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3318ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
33194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
33204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
332189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
332289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
332389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3324ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3325ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3326ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
332748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
332848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
332948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
333048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
333148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
333248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
333348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
333448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
333548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
333648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
33375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
33384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getRawContactView());
3339d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sRawContactsProjectionMap);
334089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
33414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
33424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
33434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
33445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
33455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
33464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getRawContactView());
3347d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sRawContactsProjectionMap);
334889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
334989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
33504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
33514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
33524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
33535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
33545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
33554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
33564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
335789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
335889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=" + rawContactId);
3359e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3360e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3361e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3362e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
33634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
33644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
33654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3366e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3367e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3368e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
33694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
33704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
33714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
33724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(Data._ID + "=" + ContentUris.parseId(uri));
33734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
33744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
33754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3376a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            case DATA_WITH_PRESENCE: {
3377a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView() + " data" +
3378a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
3379a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " ON (" + AggregatedPresenceColumns.CONTACT_ID + "="
3380a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContacts.CONTACT_ID + ")" +
3381a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
3382a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
3383a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + StatusUpdatesColumns.DATA_ID + ")");
3384a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                qb.setProjectionMap(sDataWithPresenceProjectionMap);
33855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3386a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3387a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3388a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3389a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
33904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3391a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3392a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3393a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3394e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
3395a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3396a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3397e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3398e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                mOpenHelper.buildPhoneLookupAndContactQuery(qb, number);
3399e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3400e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
3401e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3402e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3403e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3404a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3405a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3406a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3407ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
340889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.setTables(mOpenHelper.getGroupView());
3409ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
341089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3411ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3412ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3413ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3414ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3415ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
341689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.setTables(mOpenHelper.getGroupView());
3417ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
341889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(Groups._ID + "=" + groupId);
3419ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3420ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3421ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3422ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
342389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.setTables(mOpenHelper.getGroupView() + " AS groups");
3424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
342589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
342689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3427ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3428ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3429ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3430b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
34310c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3432b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3433b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3434b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3435b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
343631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3437d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
34382d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
34392d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
34402d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
34412d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
344231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3443d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3444d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
344531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
344631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
344731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
344831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
34497581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
34507581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
34517581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
34522d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
345331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
345431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3455eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3456eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3457eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
345889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3459e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3460e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3461e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3462e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                final String groupMembershipMimetypeId = Long.toString(mOpenHelper
3463e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
3464ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (mOpenHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3465e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3466e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3467ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (mOpenHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3468e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3469e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3470e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3471eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3472eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3473eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
34745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case PRESENCE: {
3475a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                qb.setTables(Tables.PRESENCE +
3476a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
3477a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " ON(" + Presence.DATA_ID + "=" + StatusUpdatesColumns.DATA_ID + ")");
3478373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setProjectionMap(sPresenceProjectionMap);
34795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
34805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
34815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
34825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case PRESENCE_ID: {
3483373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setTables(Tables.PRESENCE);
3484373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setProjectionMap(sPresenceProjectionMap);
3485373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.appendWhere(Presence._ID + "=" + ContentUris.parseId(uri));
34865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
34875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
34885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3489c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3490a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3491c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3492c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3493c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
3494b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3495b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
3496c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3497c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
34981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
34991b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
35001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
35011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
35021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
35031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
35041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
35051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
35061b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
35071b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
35081b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
35091b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
35101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
35111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
35121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
35131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
35141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
35151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
35161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
35171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
35181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(sContactsInGroupSelect);
35191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
35201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
35211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
35224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
3523f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3524c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
35254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
35264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
35285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
35295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
35315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
35325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
3533038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
3534038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
3535038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
3536038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
35375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
35385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
35394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
35404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
35414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
35424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
35434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
35444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
35465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
35475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
35485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
35505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (contactId == -1) {
35515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
35525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
35555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
35565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
35585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
35595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
35615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
35625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
35635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
35645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
35655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
35665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
35685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
35695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
35705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
35715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
35725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
35745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
35755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int sourceIdCount = 0;
35765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
35775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
35785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
35795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sourceIdCount++;
35805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (sourceIdCount == 0) {
35845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
35855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
35885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
35895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
35905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
35915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
35925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
35935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
35945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
35955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
35985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
35995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
36015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
36025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
36035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
36045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
36055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
36065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
36075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
36085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
36095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
36105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
36115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
36125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
36135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
36145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
36155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
36165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
36175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
36185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
36195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
36205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
36215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
36235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
36245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupByDisplayNameQuery {
36265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
36275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
36295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
36305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
36315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
36325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
36335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
36345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
36365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
36375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
36385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int NORMALIZED_NAME = 3;
36395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
36405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
36425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
36435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int displayNameCount = 0;
36445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
36455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
36465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
36475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                displayNameCount++;
36485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
36495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
36505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
36525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
36535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
36545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
36565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
36575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
36585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
36595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
36605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
36615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
36625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
36635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
36645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
36655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
36665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
36675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
36685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
36705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
36715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
36725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
36735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
36745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
36755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
36765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
36775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
36785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
36795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
36805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
36815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
36825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
36835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
36845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
36855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
36865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
36875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
36885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
36895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
36905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
36925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
36935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
36945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
36955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
36965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
36975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
36985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
36995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
37015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
37025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
37045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
37055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
37075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
37085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
37095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
37105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
37115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
37125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
37135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
37145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
37155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
37165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
37175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
37185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
37195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
37205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
37215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
37225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
37235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
37245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
37255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
37265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
37275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
37285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3729ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, String[] projection) {
3730ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        String contactView = mOpenHelper.getContactView();
3731ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        boolean needsPresence = mOpenHelper.isInProjection(projection, Contacts.PRESENCE_STATUS,
3732ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.PRESENCE_CUSTOM_STATUS);
3733ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        if (!needsPresence) {
3734ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            qb.setTables(contactView);
3735ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            qb.setProjectionMap(sContactsProjectionMap);
3736ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        } else {
3737a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            qb.setTables(contactView +
3738a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
3739a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ") " +
3740a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
3741a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
3742a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                            + StatusUpdatesColumns.DATA_ID + ")");
3743ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            qb.setProjectionMap(sContactsWithPresenceProjectionMap);
3744ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
3745ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
3746ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
3747ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
37484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
37494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
37504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
37514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
37524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
37534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
37544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
37554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
37564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
37574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
37584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
37594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
37604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3761e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
3762e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
3763e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
3764e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        if (!TextUtils.isEmpty(accountName)) {
3765e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
3766e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
3767e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
3768e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
3769e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
3770e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
3771e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
3772e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
3773e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
3774e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
3775e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
3776e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
3777e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
3778e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
3779e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
37807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
3781c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
3782c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
3783c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
3784c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
3785c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
3786c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private String getLimit(Uri url) {
3787c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limitParam = url.getQueryParameter("limit");
3788c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
3789c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
3790c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
3791c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
3792c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
3793c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
3794c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
3795c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
3796c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
3797c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3798c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
3799c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
3800c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
3801c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
3802c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
3803c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
3804c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
38055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
38065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
38075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
38085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
38095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
38105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
38115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
38125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
38135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
38145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
38155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
38165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
38175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
38185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
38195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
38205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
38215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
38225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
38235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
38245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
38255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
38265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
38275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
38285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
38295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
38305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
38315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
38325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
38335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
38345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
383500ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
38364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (mOpenHelper.hasRestrictedAccess()) {
383770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
383870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
38396cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
384070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
384170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
384270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
384370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
38444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (mOpenHelper.hasRestrictedAccess()) {
384570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
384667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
38475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
38485ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
3849619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
3850619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
3851619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3852b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
3853b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
3854b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
3855b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
3856d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
3857b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                if (!"r".equals(mode)) {
3858b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
3859b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
3860b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3861b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
3862b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3863b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                String sql =
3864b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mOpenHelper.getDataView() +
3865b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
3866b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=" + contactId;
3867b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                SQLiteDatabase db = mOpenHelper.getReadableDatabase();
3868b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql, null);
3869d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
3870d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3871d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_LOOKUP:
3872d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
3873d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // TODO: optimize lookup when direct id provided
3874d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
3875d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3876d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String selection = RawContacts.CONTACT_ID + "=" + contactId;
3877d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3878d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
3879d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
3880d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
3881d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
3882d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                outputRawContactsAsVCard(localStream, selection, null);
3883d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
3884d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
3885b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3886b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
3887b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new FileNotFoundException("No file at: " + uri);
3888b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
3889b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
3890b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3891d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
3892d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String VCARD_TYPE_DEFAULT = "default";
3893d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3894d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
3895d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
3896d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
3897d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
3898d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
3899d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
3900d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
3901d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
3902d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3903d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
3904d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
3905d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3906d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
3907d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
3908d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
3909b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3910d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
3911d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
3912d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
3913d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
3914d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
3915d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
3916d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3917d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
3918d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
3919d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
3920d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
3921d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
3922d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
3923d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
3924d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
3925d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
3926d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
3927d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3928d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        // TODO: enforce the callers security clause is used
3929d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        if (!composer.init(selection, selectionArgs))
3930d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
3931d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3932d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
3933d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
3934d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
3935d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
3936d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
3937d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
3938d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
3939b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3940bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3941bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov    private static Account readAccountFromQueryParams(Uri uri) {
3942bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        final String name = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
3943bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        final String type = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
3944bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
3945bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov            return null;
3946bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        }
3947bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        return new Account(name, type);
3948bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov    }
3949bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3950bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3951619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
39527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
39537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
39547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
3955d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static class RawContactsEntityIterator implements EntityIterator {
39567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
39577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
39587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
39607a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
39617a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
39627a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
39637a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
39647a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
39657a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
39667a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
39677a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
39687a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
39697a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
39707a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
39717a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
39727a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
39737a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
39747a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
39757a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
39767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
39777a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
39787a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4};
39797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
39816cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
39826cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
39836cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.SOURCE_ID,
39846cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.VERSION,
39856cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.DIRTY,
39867a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data._ID,
39877a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RES_PACKAGE,
39887a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.MIMETYPE,
39897a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
39907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
39917a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
39927a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
39937a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
39947a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
39957a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
39967a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
39977a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
39987a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
39997a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
40007a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
40017a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
40027a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
40037a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
40047a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
40057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
40067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
40077a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4,
40087a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RAW_CONTACT_ID,
40097a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.IS_PRIMARY,
40103cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar                Data.IS_SUPER_PRIMARY,
40117a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA_VERSION,
40127a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                GroupMembership.GROUP_SOURCE_ID,
40137a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC1,
40147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC2,
40157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC3,
401694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                RawContacts.SYNC4,
401738446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey                RawContacts.DELETED,
4018c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.CONTACT_ID,
4019c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.STARRED};
4020035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
4021035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
4022035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
4023035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
4024035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
4025035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
4026035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
402767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
402867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
402967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
40307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_RAW_CONTACT_ID = 27;
40317a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_IS_PRIMARY = 28;
40323cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_IS_SUPER_PRIMARY = 29;
40333cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DATA_VERSION = 30;
40343cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_GROUP_SOURCE_ID = 31;
40353cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC1 = 32;
40363cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC2 = 33;
40373cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC3 = 34;
40383cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC4 = 35;
40393cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DELETED = 36;
40403cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_CONTACT_ID = 37;
40413cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_STARRED = 38;
40427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4043d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        public RawContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
40447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
40457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
40467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
40477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
40487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                    ? Data.RAW_CONTACT_ID
40497a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                    : (Data.RAW_CONTACT_ID + "," + sortOrder);
40507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
40517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
40527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4053226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setTables(Tables.CONTACT_ENTITIES);
40547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
40555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.appendWhere(Data.RAW_CONTACT_ID + "=" + contactsIdString);
40567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
40576cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
40586cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
4059035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
406023c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                if (contactsIdString != null) {
406123c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                    qb.appendWhere(" AND ");
406223c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                }
40636cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
4064035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
40656cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                        + RawContacts.ACCOUNT_TYPE + "="
4066035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
4067035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
40687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
40697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
40707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
40717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
40727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4073038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4074038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4075038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4076038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4077038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4078038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4079038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana
40807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
40817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
40827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
40837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
40847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
40857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
40867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
40877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
40887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
40897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
40907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
40917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
40927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
40937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
40947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
40957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
40967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
40977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
40987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
40997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
41007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
41017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
41027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
41037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
41047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
41057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
41067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
41077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
41087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
41097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
41106cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
41116cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
41125ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
41136cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
41146cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
41156cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
41167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
41177a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
41187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
41197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
412094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            contactValues.put(RawContacts.DELETED, c.getLong(COLUMN_DELETED));
412138446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey            contactValues.put(RawContacts.CONTACT_ID, c.getLong(COLUMN_CONTACT_ID));
4122c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            contactValues.put(RawContacts.STARRED, c.getLong(COLUMN_STARRED));
41237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
41247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
41257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
41267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
41277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
41287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
41297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
413023c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                if (c.isNull(COLUMN_CONTACT_ID)) {
413123c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                    continue;
413223c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                }
41337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
41347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
413523c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data._ID, c.getLong(COLUMN_DATA_ID));
41367a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
41377a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
413823c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_PRIMARY, c.getLong(COLUMN_IS_PRIMARY));
413923c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_SUPER_PRIMARY, c.getLong(COLUMN_IS_SUPER_PRIMARY));
41407a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
41419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
41429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
41439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
41449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
41457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
41467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                for (int i = 0; i < DATA_KEYS.length; i++) {
41477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
41487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
41497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
41507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
41517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
41527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
41537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
41547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
41557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
41567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
41577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
41587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
41597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
41607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
41617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
41627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
41637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
41647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
41657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
41667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
41677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4168226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    /**
4169226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
4170226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
4171226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     */
4172226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    private static class GroupsEntityIterator implements EntityIterator {
4173226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private final Cursor mEntityCursor;
4174226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private volatile boolean mIsClosed;
4175226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4176226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final String[] PROJECTION = new String[]{
4177226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups._ID,
4178226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_NAME,
4179226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_TYPE,
4180226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.SOURCE_ID,
4181226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.DIRTY,
4182226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.VERSION,
4183226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.RES_PACKAGE,
4184226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE,
4185226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE_RES,
41867a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.GROUP_VISIBLE,
41877a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC1,
41887a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC2,
41897a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC3,
41907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC4,
41917a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYSTEM_ID,
419294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.NOTES,
419394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.DELETED};
4194226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4195226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ID = 0;
4196226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_NAME = 1;
4197226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 2;
4198226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_SOURCE_ID = 3;
4199226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_DIRTY = 4;
4200226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_VERSION = 5;
4201226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_RES_PACKAGE = 6;
4202226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE = 7;
4203226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE_RES = 8;
4204226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_GROUP_VISIBLE = 9;
42057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 10;
42067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 11;
42077a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 12;
42087a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 13;
42097a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYSTEM_ID = 14;
42107a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_NOTES = 15;
421194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        private static final int COLUMN_DELETED = 16;
4212226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4213226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
4214226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
4215226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = false;
4216226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4217226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String updatedSortOrder = (sortOrder == null)
4218226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    ? Groups._ID
4219226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    : (Groups._ID + "," + sortOrder);
4220226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4221226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
4222226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
422389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            qb.setTables(provider.mOpenHelper.getGroupView());
4224226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setProjectionMap(sGroupsProjectionMap);
4225226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (groupIdString != null) {
4226226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups._ID + "=" + groupIdString);
4227226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4228226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountName = uri.getQueryParameter(Groups.ACCOUNT_NAME);
4229226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountType = uri.getQueryParameter(Groups.ACCOUNT_TYPE);
4230226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!TextUtils.isEmpty(accountName)) {
4231226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups.ACCOUNT_NAME + "="
4232226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4233226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + Groups.ACCOUNT_TYPE + "="
4234226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
4235226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4236226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
4237226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    null, null, updatedSortOrder);
4238226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToFirst();
4239226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4240226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4241226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public void close() {
4242226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4243226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("closing when already closed");
4244226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4245226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = true;
4246226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.close();
4247226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4248226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4249226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public boolean hasNext() throws RemoteException {
4250226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4251226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
4252226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4253226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4254226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return !mEntityCursor.isAfterLast();
4255226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4256226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4257038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4258038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4259038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4260038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4261038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4262038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4263e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4264226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public Entity next() throws RemoteException {
4265226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4266226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
4267226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4268226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!hasNext()) {
4269226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
4270226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4271226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4272226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
4273226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4274226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final long groupId = c.getLong(COLUMN_ID);
4275226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4276226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            // we expect the cursor is already at the row we need to read from
4277226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            ContentValues groupValues = new ContentValues();
4278226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4279226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4280226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups._ID, groupId);
4281226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
4282226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
4283226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4284226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4285226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
4286226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
4287226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
42887a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
42897a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
42907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
42917a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
42927a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
429394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            groupValues.put(Groups.DELETED, c.getLong(COLUMN_DELETED));
42947a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
4295226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            Entity group = new Entity(groupValues);
4296226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4297226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToNext();
4298226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4299226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return group;
4300226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4301226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    }
4302226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4303a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
43047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
43057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
4306568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
4307568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
43087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
43097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
43105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS:
43115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID:
43127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
43135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (match == RAW_CONTACTS_ID) {
43147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
43157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
43167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4317d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return new RawContactsEntityIterator(this, contactsIdString,
43187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
4319226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS:
4320226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS_ID:
4321226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String idString = null;
4322226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                if (match == GROUPS_ID) {
4323226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    idString = uri.getPathSegments().get(1);
4324226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                }
4325226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4326226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                return new GroupsEntityIterator(this, idString,
4327226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        uri, selection, selectionArgs, sortOrder);
43287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
43297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
43307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
43317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
43327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
43334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
43344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4335a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
43364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4337b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4338b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP:
4339be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
4340b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4341b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4342b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4343b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4344be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4345b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4346b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4347508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4348b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return mOpenHelper.getDataMimeType(ContentUris.parseId(uri));
434948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
435048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
435148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
435248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
435348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
435448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
435548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
435648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
435748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
435848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
435948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
436048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4361b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4362b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4363b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4364b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4365b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4366b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4367b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4368b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4369c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4370c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4371c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4372c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
437361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
437461efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
43754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
43764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
43777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
437825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
43793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
438025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
43813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
438225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindNull(1);
43833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
438425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
438525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
438625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
43873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
43883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
438973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
439073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
439173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
439273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
439373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.bindLong(1, rawContactId);
439473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.execute();
439573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
439673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4397c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4398c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
4399c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
4400c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4401c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4402c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4403653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
4404c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
4405653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
4406653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
4407c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
4408c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4409c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
4410c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4411c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
4412c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
4413c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4414c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4415c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4416653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
4417c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
4418653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
4419653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
4420c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
4421c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4422ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4423f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
4424f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
4425f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4426f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4427f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4428f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
4429f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokens.length == 0) {
4430f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4431f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4432f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4433f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String address = tokens[0].getAddress();
4434f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int at = address.indexOf('@');
4435f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (at != -1) {
4436f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            address = address.substring(0, at);
4437f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4438f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4439f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4440f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
4441f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4442f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4443f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4444f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
4445f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4446f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
4447f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
4448f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4449f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4450f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4451f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4452f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
4453f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4454f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4455a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
4456a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
4457a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
4458a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4459a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
4460a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4461a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
4462a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4463a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
4464a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4465a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
4466f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4467f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
4468f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
4469f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4470f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4471f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4472f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
4473f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4474f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
4475f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SoftReference<String[]> ref;
4476f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4477f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        synchronized (mNicknameClusterCache) {
4478f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
4479f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
4480f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                if (ref == null) {
4481f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    return null;
4482f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4483f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = ref.get();
4484f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4485f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4486f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4487f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (clusters == null) {
4488f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
4489f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
4490f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
4491f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
4492f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4493f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4494f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4495f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4496f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4497f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
4498f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
4499f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4500f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
4501f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
4502f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                null, null, null);
4503f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
4504f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
4505f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (count > 0) {
4506f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = new String[count];
4507f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int i = 0; i < count; i++) {
4508f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    cursor.moveToNext();
4509f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
4510f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4511f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4512f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        } finally {
4513f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            cursor.close();
4514f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4515f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4516f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4517f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4518f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
4519f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4520f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
4521f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
4522f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4523f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4524f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4525f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
4526f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
4527f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
4528f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4529f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4530f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4531f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
4532f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
4533f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4534f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4535f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4536f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4537f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
4538f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4539f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
4540f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
4541f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
4542f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
4543f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
4544f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
4545f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4546f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4547f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4548f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
4549f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4550f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
4551f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
4552f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
4553f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4554f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
45552d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
4556d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4557d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
4558d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
4559d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
4560d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
4561d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
4562d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
4563e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
4564d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN("
4565d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
4566d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
4567d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NICKNAME + ","
4568d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.ORGANIZATION + "))");
4569e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
4570e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
45715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
4572c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4573c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
4574c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
4575c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4576c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4577a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
4578c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            String limit) {
45795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), limit);
45805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
45815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
45825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
45835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            String limit) {
4584d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4585d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + NameLookupColumns.RAW_CONTACT_ID +
4586d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
4587d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
4588d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
45895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
4590a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
4591a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
4592a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
4593d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
4594a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.ORGANIZATION + ")");
45953de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
4596c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limit != null) {
4597c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
4598c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4599c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(")");
4600ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
4601ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
46024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
46034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
46044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
46054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
4606b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
4607b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
4608b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
4609b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
4610b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
46114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
46124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
4613b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
4614b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
4615b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
4616caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
4617caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
4618caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
4619caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
4620df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
4621df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
4622caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
4623caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
4624caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
4625caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
46266f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
4627caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
46286f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
4629caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
46304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
4631