ContactsProvider2.java revision 07ce1624de652aa8494630a071b051a1670c4e3d
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;
21b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
22b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
23b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
24b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkeyimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
26b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
27b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DisplayNameSources;
28b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
29b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
30b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
31b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
32b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NicknameLookupColumns;
33b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
34b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
35b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
36b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
37b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
38b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
39b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
40a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Lists;
41a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Maps;
42a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Sets;
433de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
44b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
45caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
465b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
47c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
48568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
49568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
506ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
5435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
5567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.EntityIterator;
56627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
57568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
59627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
613de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.content.SharedPreferences.Editor;
62b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
631129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikovimport android.database.CharArrayBuffer;
644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
65ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
66a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
67b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
68b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
71c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
736ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
74d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
75b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
760e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
77d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.pim.vcard.VCardComposer;
783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
79508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
803de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
813de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.LiveFolders;
823de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.OpenableColumns;
833de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.SyncStateContract;
84b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
863de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
873de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
883de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
893de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
903de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
9182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
93ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
94ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
98de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
99b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1004097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
10167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
102a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
103a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
104f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Token;
105f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Tokenizer;
106c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
108d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
109b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
110d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
111d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
112f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport java.lang.ref.SoftReference;
1137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
114315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikovimport java.util.BitSet;
1155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
116b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1170e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
119622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
120b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1210e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
122ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1285b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
129caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
130bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
131bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
132bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
134619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
135619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
136619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
1413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * Shared preference key for the legacy contact import version. The need for a version
1423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
1453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final String PREF_CONTACTS_IMPORTED = "contacts_imported_v1";
1463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int PREF_CONTACTS_IMPORT_VERSION = 1;
1473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1480e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1490e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
150a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final String TIMES_CONTACED_SORT_COLUMN = "times_contacted_sort";
1535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
154d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
1555e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            + TIMES_CONTACED_SORT_COLUMN + " DESC, "
1569b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
157d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
158d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
159d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
160d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
161d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
162d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
171f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
1724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
17646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
1774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
180ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
18148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
18248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
18348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
18448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
18548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
18648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
18748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
18848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
189a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
192b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
193b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
194b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
19582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
19682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
1971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
19831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
19931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
200eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
201eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
202ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
203ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
204ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
205ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
20635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
207b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
20835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
209c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
210c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
211c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
21746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
21846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
219d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
220f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
221f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
222f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
22367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
22467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2256cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
227f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
228ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
229ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
230d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
23167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
232d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
233ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2341f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
23514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
23667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2415ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
243f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
24488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
24588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
24688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
24788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
24888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
24988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
25088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
251f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
25414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
258f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
262321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
26320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
26420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
265321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
266321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
26720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
26820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
269f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
27019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
27119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
27219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
27319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
274ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
275ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
276ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
27719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
27819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
27919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
280ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
281ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
28219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
28319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
284c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
285df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
286caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
28771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
28871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
28971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
29071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
29171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
29271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
29371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
29471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
29571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
29671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
29771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
29871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
29971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
30071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
301a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
302a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
303a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
304a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
305a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
306a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
307a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
308a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
309a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
310a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
311a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
312a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
313038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
314038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
315e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
3175e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
3185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentStarredProjectionMap;
3195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentFrequentProjectionMap;
320f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
321f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final HashMap<String, String> sContactsVCardProjectionMap;
322ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
323d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
32446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    /** Contains the columns from the raw contacts entity view*/
32546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final HashMap<String, String> sRawContactsEntityProjectionMap;
3264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
331e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
332ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
333ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
334ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
335ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
336373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
337b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
338eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
339eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
34082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
34182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final HashMap<String, String> sStatusUpdatesProjectionMap;
3421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
3469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
3479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
3489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
3499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
3509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
351c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
352c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
354c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
355ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    /** Precompiled sql statement for incrementing times contacted for a contact */
356ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    private SQLiteStatement mContactsLastTimeContactedUpdate;
3573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
35825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
35982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
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;
3660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
367a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
368a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
369f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
370f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
3711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
3721129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdOrganization;
3731129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdNickname;
3741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdPhone;
375f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
3761129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
3771129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
3781129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs3 = new String[3];
379f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
380f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
3824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
383a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
384d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
385d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
386d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
3873653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
3883653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3892d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
3902d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3913653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
3925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
3935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
3945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
395f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
3965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
397ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
398ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
3995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
4003653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
4025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
4035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
40446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
40546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
40646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
407b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
4084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
4094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
410ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
41148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
4125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
413ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
4144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
41548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
4165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
4175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
4184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
419ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
42048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
4211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
422ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
423ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
425ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
42635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
427b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
428b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
42935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
430a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
431b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
432b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
433b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
434b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
436eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
437eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
43882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
43982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
4401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
441c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
442c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
443c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
444c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
445c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
446c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
447c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4481b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
4491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
4501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
4511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
4521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
4531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
4541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
4551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
45619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
45719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
45819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
459038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
460038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
461e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
4634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
4644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
4654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
4664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
4674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
4684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
4694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
4704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
471f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
4724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
4735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
474f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
4753296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
4763296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_PRESENCE,
4773296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
4783296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS,
4793296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
4803296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
4813296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
4823296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
4833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
4843296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_LABEL,
4853296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
4863296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_ICON,
4873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
4883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
4895e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
4905e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
4915e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Long.MAX_VALUE + " AS " + TIMES_CONTACED_SORT_COLUMN);
4925e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
4935e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
4945e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
4955e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Contacts.TIMES_CONTACTED + " AS " + TIMES_CONTACED_SORT_COLUMN);
4965e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
497f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap = Maps.newHashMap();
498f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
499d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
500f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.SIZE, "0 AS " + OpenableColumns.SIZE);
5014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
5034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
5044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
5104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
5114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
5124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
5134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
5144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
5154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
5164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
5174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
5184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
5194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
5204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
5214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
5222815f58f72f109790585931f601a63ddc02536a5Evan Millar
5234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
5244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
5254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
5284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
5294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
5304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
5314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
5324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
5334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
5344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
5354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
5364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
5374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
5384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
5394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
5404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
5414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
5424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
5434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
5444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
5454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
5464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
55082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sDataProjectionMap.put(Data.CONTACT_ID, Data.CONTACT_ID);
5514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
55656d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
564a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
5654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
566a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
56746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        HashMap<String, String> columns;
56846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns = new HashMap<String, String>();
56946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts._ID, RawContacts._ID);
57046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
57146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
57246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
57346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
57446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.VERSION, RawContacts.VERSION);
57546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DIRTY, RawContacts.DIRTY);
57646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DELETED, RawContacts.DELETED);
577bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey        columns.put(RawContacts.IS_RESTRICTED, RawContacts.IS_RESTRICTED);
57846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
57946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
58046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
58146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
58246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
58346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.MIMETYPE, Data.MIMETYPE);
58446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA1, Data.DATA1);
58546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA2, Data.DATA2);
58646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA3, Data.DATA3);
58746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA4, Data.DATA4);
58846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA5, Data.DATA5);
58946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA6, Data.DATA6);
59046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA7, Data.DATA7);
59146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA8, Data.DATA8);
59246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA9, Data.DATA9);
59346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA10, Data.DATA10);
59446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA11, Data.DATA11);
59546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA12, Data.DATA12);
59646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA13, Data.DATA13);
59746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA14, Data.DATA14);
59846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA15, Data.DATA15);
59946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC1, Data.SYNC1);
60046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC2, Data.SYNC2);
60146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC3, Data.SYNC3);
60246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC4, Data.SYNC4);
60346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
60446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.STARRED, Data.STARRED);
60546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
60646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
60746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
60846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
60946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        sRawContactsEntityProjectionMap = columns;
61046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
6113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
6133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
6153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
6233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
6243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
6263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.PRESENCE,
6273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
6283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS,
6293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
6303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
6313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
6333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
6353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_ICON,
6373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
6383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
6405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
6415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
6425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
6435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
6445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
6455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
6465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
6485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
6495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
6505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
6515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
6525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
6535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
6545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
6555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
6565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
6575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
6585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
6595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
6605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
6615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
6625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
6635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
6645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
6655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
6665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
6675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
6688f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
6695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
6715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
6725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
6735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
6745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
6755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
676a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
6775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
6785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
6795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
6803296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6813296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
6823296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
6843296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6853296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6863296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6893296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6903296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6913296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
6923296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
6933296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6943296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
6953296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
6963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
6973296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS,
6983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
6993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
7003296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
7023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
7043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
7063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
7073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
708e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
709e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
710e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
71156d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
71256d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar                Contacts.LOOKUP_KEY + " AS " + PhoneLookup.LOOKUP_KEY);
713e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
714e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
715e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
716e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
717e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
718e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
719e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
720e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
721e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
722e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
723e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
724e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
725e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
726e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
727e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
728e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
729e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
730e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
731e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
732e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
733e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
734e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
735e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
736e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
737e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
738e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
7399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
741ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
74289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
743035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
744035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
7459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
7469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
7479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
74889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
749ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
75067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
751ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
7523cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
75394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
7543cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
75538446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
75689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
75789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
75889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
75989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
760ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
761ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
7626cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
763ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
764ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
765d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
766d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
767ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
768ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
769ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
770d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
771d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
772ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
773f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
774ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
775ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
776b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
777b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
778b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
779b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
7800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
7810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
782b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
783b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
784eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
785eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
786eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
787eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
788eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
789eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
790341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
791341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
792341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
793fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
794341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
795341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
796341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
79768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
79868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
79968936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
80068936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
80168936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
802e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
80368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
80468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
80568936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
806eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
807eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
808373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
8094dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
8100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
8110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
81282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
81382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
81482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
81570c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
81670c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
81782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
81882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
81982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
82082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
8210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
8220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
8230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
8240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
8250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
82682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sStatusUpdatesProjectionMap = columns;
82719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
8281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
8291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
8301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
8311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
8321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
8331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
8341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
8351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
8361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
8371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
8381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
8394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
8404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
8413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
8423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        map.put(toField, fromField + " AS " + toField);
8433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    }
8443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
8453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
8463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
8473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
8483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
8493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
851653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
8523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
8543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
8553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
856a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
857a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
858a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
859a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
860a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
861a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
862a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
863a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
8643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
866653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
867653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
868b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
869653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
870653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
871653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
872653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
8733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
8753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
8765ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
877e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
878e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
879e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
880e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
881653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
882e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
883e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
884e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
8853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
8893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
8903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
891653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
892f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
89314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
89414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
895653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
896653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
897653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
898653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
899653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
900653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
901653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
902653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
903653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
904653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
905653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
906653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
907653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
908653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
909653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
910653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
911653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
9124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
9134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
914653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
915653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
916f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
917653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
918653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
9193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
92214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
92314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
92414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
9254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
9264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
9274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
9284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
9293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
9305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
9313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
9333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
9364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
937e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
938e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
9394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
9404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
9414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
9424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
9434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
9444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
9453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
946e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
94714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
948f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
949e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
950e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
951e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
952e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
9533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
9543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
9553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
9563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
9584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
9594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
960e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
961e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
962e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
963e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
964e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
965e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
966e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
967e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
9683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
97025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
971285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
972d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
973285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateDisplayName(db, rawContactId);
974285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
9753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
976a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
977a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
978a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return true;
979a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
980622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
981622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
982622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
983622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
984622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
985622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
986622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
987622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
9884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
9894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
9904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
991622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
992622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
993622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
994622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
995622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        values.put(key, cursor.getString(i));
996622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
997622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
998622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
999622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1000622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1001622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1002622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1003622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
10073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
10093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1014622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
10153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1016622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
10173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1018622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
10193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1023622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
102414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
102514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
102614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1027f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1028f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
102925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
103014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
103114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
103214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
103314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
103414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1035f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1036622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1037622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1038cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1039622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1040622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
104114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1042f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
104314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1044f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
1045f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
1046f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
1047f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
104814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
104925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
105014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
105114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
105214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
105314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
105414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
105514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
105614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
105714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
105814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1059f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
106025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
106114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
10623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1065622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
10663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1067622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1068622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1069622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1070622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
10713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1072622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1073622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1074622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1075622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1076622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1077622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1078622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
107967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1080622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
108167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
108267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1083622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1084622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
10858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1086622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1087622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
108867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
108967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
109067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
109167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
109267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
109367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
10948c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1095622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
1096622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(name);
1097622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
1098622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1099622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1100622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1101622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1102622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1103622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1104622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1105622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1106622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1107622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1108622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1109622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1110622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1111622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1112622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1113622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1114622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1116622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1117622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1118f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1119622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1120622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1121622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1122f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1123622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1124622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1125622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1126622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1127622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1128622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1130622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1131622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1132622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1133622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1134622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1135622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1136622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1137622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1138622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1139622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1140622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
114167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
114267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
114367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
114467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1145622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1146622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1147622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1148622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1149622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1150622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
115167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
115267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
115367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1154622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1155622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1156622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
11573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
11583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
11623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
11643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
11653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
11673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
11683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
11693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
11703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1174622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1175622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1176622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1178622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1179622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1180f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1181622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1182622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1183622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1184f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1185622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1187622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1188622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1189622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1190622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1192622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1193622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1194622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
11953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1196622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1197622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1198622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1199622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1200622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
12013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
12053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
12073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
12083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1212a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1213a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1214a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1215a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1216a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
121725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1218a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1219a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
12203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
122314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1224f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1225a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1226a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1227a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
122814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
122914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1230f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
123114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
123225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1233a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
1234a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
123514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
123614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
123714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
123814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1239a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
124014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
124114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
124325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1244a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
124514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
124614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
124714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
12493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
12503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
12513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
12523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
12533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
12543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
12553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1257a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1258a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1259a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1260a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1261a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
12623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1264e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1265e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1266e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1267e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1268e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1269e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1270e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
12715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
127214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
127314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
127414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
127514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
127625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1277f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
127814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
127914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
128014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
128114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
128214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1283f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
128414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
128514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
128614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
128714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1288f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
128914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1290f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1291f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
129225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
129314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
129414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
129614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
129714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
129814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
129914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
130014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
130114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1302f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
130325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
130414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1305e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1306e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1307e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1308e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1309e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1310e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1311e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1312e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1313e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1314e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1315e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1316e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1317e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1318e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
131914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
132014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
132214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
132314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
132414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
132614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
132714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
132814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
133014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1332f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
133314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
133414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
133514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
133714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1338f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
133914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
134014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
134114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
134214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1343f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
134414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1345f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1346f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
134725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
134814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
134914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
135114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
135214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
135314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
135414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
135614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1357f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
135825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
135914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
136014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
136114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
136214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
13633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
13643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
13663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
13673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
13710b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
13720b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
13730b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
13740b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1375653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13760b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1377653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13780b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1379285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
138025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
13810b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
13820b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
13830b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1384653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri 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 dataId = c.getLong(DataUpdateQuery._ID);
139114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
13920b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
13930b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
13940b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1395653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1396f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
1397653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13980b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1399285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
140025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
14010b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1402f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
14030b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
140414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
140514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
140614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
140714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
140814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
140914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
141014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
141214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1414285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
141525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
141614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1417653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1418653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1419653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1420e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1421e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1422e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1423e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1424653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1425653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1426653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1427e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1428653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1429653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1430e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1431653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
14325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1433653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1434e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
143536045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.MIN_MATCH,
143636045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                        PhoneNumberUtils.toCallerIDMinMatch(number));
143736045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov
1438653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1439653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
14404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
14414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1442e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
14433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
14463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
14483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
14493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
14503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
14513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
14523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
14533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
14543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
14553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
14563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
14573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1461653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1462653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1463653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1464653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1465653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1466653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1467653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1468653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1469653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
14700be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
14710be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
14720be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1473653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1474653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1475653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1476653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1477f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
147814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1479653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1480f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
14810be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
14820be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
14830be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14840be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
14850be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
14860be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
14870be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
14880be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
14890be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
14900be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
14910be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14920be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1493b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
14940be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1495b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
14960be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1497653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1498653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1499653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1500653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1501653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1502653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1503653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1504653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1505653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1506653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1507653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1508653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1509653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1510653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1511653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1512653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1513653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1514653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1515653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1516653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1517653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1518653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1519653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1520653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1521ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1522ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1523653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1524653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1525653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1526653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1527a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1528a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1529a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1530a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1531a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1532653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1533653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1534a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1535a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1536a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1537a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1538a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1539a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1540a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1541a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1542a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1543285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1544285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1545285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1546a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1547a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1548a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1549a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1550a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1551f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1552a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1553f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1554a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1555a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1556a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1557a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1558a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1559a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1560a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1561a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1562a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1563a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1564a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1565a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1566a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1567a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1568a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1569a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1570a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1571ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
1572ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1573ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1574ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1575ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    public class GroupIdCacheEntry {
1576ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1577ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1578ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1579ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1580ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1581a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
15823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1583b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
158431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
15854097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1586f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1587315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1588315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    // We will use this much memory (in bits) to optimize the nickname cluster lookup
1589315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private static final int NICKNAME_BLOOM_FILTER_SIZE = 0x1FFF;   // =long[128]
1590315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private BitSet mNicknameBloomFilter;
1591315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1592ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache = Maps.newHashMap();
1593ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
1594622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1595622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1596ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1597ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1598ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // is a list of groups with this group id.
1599ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1600ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
1601622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1602f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1603a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1604a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
160520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
16061129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
160720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1608ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
160973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1610ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1611b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1612a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1613b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1614de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
16151a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
16161a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
161781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
161881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
16194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
16204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1621de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1622ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1623ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1624ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1625ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1626ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1627ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1628ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
162935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1630ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1631de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1632b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1633a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1634b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1635d076a108d58b30591f197e1b90fa8de60999c499Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper);
16360e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1637a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1638b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1639653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1640c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1641653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1642653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1643653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1644653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1645653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1646c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1647653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1648653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1649653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1650653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1651653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1652653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1653653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1654653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1655653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1656653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1657653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1658ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate = db.compileStatement(
1659ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "UPDATE " + Tables.CONTACTS +
1660ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
1661ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "WHERE " + Contacts._ID + "=?");
1662a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
166325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate = db.compileStatement(
166425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
166525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
166625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
166725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
16683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1669a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mLastStatusUpdate = db.compileStatement(
1670a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                "UPDATE " + Tables.CONTACTS +
1671a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1672a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1673a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1674a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1675a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1676a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1677a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1678a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1679a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1680a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
16810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
16820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1683a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                        " LIMIT 1)" +
1684a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1685e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1686622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        final Locale locale = Locale.getDefault();
168728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
168828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
168928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
169028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
1691622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1692622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                locale);
1693f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1694622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
16954097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1696f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1697f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1698f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1699f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1700f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1701f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1702f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1703a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateInsert = db.compileStatement(
1704a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1705a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1711a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1712a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateReplace = db.compileStatement(
1713a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1714a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
17160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1721a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1722a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateAutoTimestamp = db.compileStatement(
1723a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
17250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1726a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
17270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
17280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
17290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mStatusAttributionUpdate = db.compileStatement(
17300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
17320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
17330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
17340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1735a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1736a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateDelete = db.compileStatement(
1737a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1738a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1739a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
17403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
17413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1742e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
17433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
17443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
174567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
174667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
17473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
17483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
174914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
17503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
17513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1752622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1753622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1754a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1755a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
17563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
17573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1758568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            importLegacyContactsAsync();
17593d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1760568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1761c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        verifyAccounts();
176270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
1763f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1764f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
17651129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
17661129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
17671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
17681129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
1769315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        preloadNicknameBloomFilter();
17701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
17714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
17724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1773c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
1774c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1775c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1776c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
1777c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
177831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1779de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1780b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1781b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
178231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
178331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1784013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1785013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1786013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1787013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
17883d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
17893d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
17903d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
17913d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17923d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1793568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1794568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1795568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1796568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1797568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1798568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
1799568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
1800568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1801568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
1802ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        mAccessLatch = new CountDownLatch(1);
1803568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1804568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
1805568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
1806568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
1807568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                if (importLegacyContacts()) {
1808d076a108d58b30591f197e1b90fa8de60999c499Dmitri Plotnikov                    // TODO aggregate all newly added raw contacts
1809568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1810568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    /*
1811568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * When the import process is done, we can unlock the provider and
1812568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * start aggregating the imported contacts asynchronously.
1813568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     */
1814ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch.countDown();
1815ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1816568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
1817568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1818568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
1819568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1820568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
1821568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1822568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18233d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
1824568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1825568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        if (importLegacyContacts(importer)) {
18263d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18273d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
18283d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
18293d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
18303d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18313d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
18323d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
18333d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18343d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18353d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
18363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1837568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
18380e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
18393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
18403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
18413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
18420e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff            mContactAggregator.setEnabled(aggregatorEnabled);
18433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
18453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
18463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
18473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1850a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1851a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1852a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1853a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1854b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1855a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1856a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1857568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1858568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1859568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1860568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1861568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1862568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1863568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1864568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
1865ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
1866ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
1867ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
1868ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
1869ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
1870ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1871ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
1872ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
187381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
1874ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
1875ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1876568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1877568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1878568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1879568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1880568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1881568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1882568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1883568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1884568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1885568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1886568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1887568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1888568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1889568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1890568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1891568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1892568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
1893568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1894568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1895568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1896568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1897568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1898568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1899568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
1900568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1901568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1902568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1903568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1905285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1906bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1907b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1908b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1909285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
19101ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1911b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1912b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1913b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1914b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
1915285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
1916b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
1917df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
1918a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
1919285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1920285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1921285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1922285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
19231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1924bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1925b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1926b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1927285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1928b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
19291ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
19301a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
19311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1932b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
19331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
1934b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1935b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1936b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1937bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1938b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1939b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
19401129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
194108e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
1942d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
1943d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
1944285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1945b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1946a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
1947a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1948a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1949a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
1950a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1951a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1952a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1953a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1954b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
1955a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1956a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1957a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
1958a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1959a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1960b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1961b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1962b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
1963b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
1964b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().update(mDb, id, entry.getValue());
1965b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1966b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1967b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1968b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1969b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1970a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1971a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1972a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1973a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1974a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
1975b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1976a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1977b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1978a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1979a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1980285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1981285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1982285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1983cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
198481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
198581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
198681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
198781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
198881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
198981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
199081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1991cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1992568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1993285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
1994ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
1995285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1996285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
19973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
19983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
19993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
20003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
20013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
20023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
20033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
20043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
20053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
20064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2007de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2008bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
20091129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2010b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2011f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2012f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2013f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2014f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2015a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2016a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
201735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2018a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
201935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2020b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
202135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
202235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2023d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2024d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
20256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
20276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
20285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2029f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertRawContact(uri, values);
2030f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2031a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2033a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20345ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
20355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2036f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2037f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2038a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2039a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2040a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2041a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2042f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2043f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2044a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2045a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2046a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2047ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2048f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2049f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2050ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2051ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2052ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2053eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
20545aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
205543880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2056eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2057eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2058eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
205982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
206082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
20611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
20621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
20631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2064a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
206581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2066f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2067a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2068a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
20707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
20717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
20727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2073de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2074a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2075a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2076a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2077035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
2078035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
2079f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the ContentValues to read from and update
2080f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the explicitly provided Account
2081f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @return false if the parameters are inconsistent
20827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2083f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private boolean resolveAccount(Uri uri, ContentValues values) {
2084f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2085f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2086f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2087f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2088f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = null;
2089f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = null;
2090f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2091f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2092f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2093f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2094f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2095f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(valueAccountName) && TextUtils.isEmpty(valueAccountType)) {
2096f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2097f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2098f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
2099f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (accountName != null && !accountName.equals(valueAccountName)) {
2100f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return false;
2101f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
2102f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2103f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (accountType != null && !accountType.equals(valueAccountType)) {
2104035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
2105035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
2106f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2107f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2108f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2109f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2110f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2111f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2112f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = null;
2113f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return true;
2114035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2115f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2116f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2117f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2118f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2119f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2120035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2121f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2122035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
21237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
21247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2126d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
21276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
21286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
21296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
21306bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2131d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2132de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
21336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
21346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2136a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2137a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2138f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2139f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2140a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2141a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2142f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertRawContact(Uri uri, ContentValues values) {
2143f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2144f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2145f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2146f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2147f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
21487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
21497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
21523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2153f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
21543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
21553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2156f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2157023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
2158285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2159285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2160f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mInsertedRawContacts.put(rawContactId, mAccount);
2161f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2162023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2163a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2164a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2165a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2166a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2167a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2168a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2169a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2170a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2171f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2172a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2173de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2174de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
217567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2176de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
217720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2178de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2179de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2180de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2181b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2182de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2183de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2184508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2185de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2186de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2187de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2188de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2189de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
21904097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2191b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2192de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2193a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2194a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2195a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2196f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2197de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2198a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2199b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2200a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2201a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2202a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2203a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2204a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
22054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
22064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22078e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
22088e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
22098e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
22108e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
22118e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2212b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2213f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
22148e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
22158e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
22168e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22178e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2218421782cb554e5050cf62a86b98df6520038dcd15Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId);
2219f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22208e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
22218e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22228e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2223b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2224f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
22258e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
22268e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
22278e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2228f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22298e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2230f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2231c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2232b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
22338e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2234f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22358e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2236f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2237f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2238f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2239a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
22405ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
22419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
22429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
22435ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
22449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
22459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
22469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
22479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
22489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2249ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2250ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2251ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2252ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
22534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
2254ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
22554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
2256ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2257ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2258ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2259ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2260ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2261ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2262ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
22639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2264ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2265ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
22669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
22679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2268ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
22699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
22709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2271ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
22729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
22739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
22749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2275ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2276ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2277ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2278ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2279ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2280ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2281ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2282ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2283ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2284ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2285ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2286ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2287ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2288ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2289ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2290ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2291ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2292ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2293ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2294ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
22959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
22965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2297ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
22989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2299df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
23009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2301ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2302ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
23039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
23049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2305df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2306df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
23079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
23089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
23099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
23109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
23119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
23129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2313ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
23149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
23159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
23169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
23179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2318ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2319ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
23209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
23219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2322d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
23231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
23241129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
23251129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
23261129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
23271129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
23281129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Organization.TITLE +
23291129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
23301129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
23311129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
23321129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2333d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2334d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2335d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
2336d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int DATA = 2;
2337d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int TITLE = 3;
2338d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2339d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2340d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2341d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2342d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2343d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2344d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2345d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        String bestDisplayName = null;
2346d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
2347d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
23481129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
23491129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2350d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2351d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
23521129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
23531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
23541129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                // Display name is at DATA1 in all type. This is ensured in the
23551129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                // constructor.
23561129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                mCharArrayBuffer.sizeCopied = 0;
23571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                c.copyStringToBuffer(DisplayNameQuery.DATA, mCharArrayBuffer);
23581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                if (mimeType == mMimeTypeIdOrganization && mCharArrayBuffer.sizeCopied == 0) {
23591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
2360d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
23611129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
23621129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                if (mCharArrayBuffer.sizeCopied != 0) {
23631129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    int source = getDisplayNameSource(mimeType);
23641129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    if (source > bestDisplayNameSource) {
2365d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
23661129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
23671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
23681129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    } else if (source == bestDisplayNameSource
23691129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            && source != DisplayNameSources.UNDEFINED) {
23701129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        if (mimeType == mMimeTypeIdStructuredName
23711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                || c.getInt(DisplayNameQuery.IS_PRIMARY) != 0) {
23721129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
23731129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
23741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
23751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2376d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
2377d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2378d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2379d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2380d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2381d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2382d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2383d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2384d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
2385d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2386d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
23871129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
23881129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
23891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
23901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
23911129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
23921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
23931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
23941129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
23951129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
23961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
23971129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
23981129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
23991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
24001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
24011129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
24021129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
24039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
240420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
240520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2406f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
240720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
240820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2409de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2410de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
241114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2412de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2413de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
241414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
241514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2416a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2417a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2418f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
241988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
2420a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    if (rowHandler.isAggregationRequired()) {
2421a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        triggerAggregation(rawContactId);
2422a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    }
242388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
242420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
242520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2426de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
242720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
242820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
242920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
243020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
243120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
243288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
243388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
243488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
243520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2436f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
243788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
243888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
24394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
24404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
24414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2442f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
244320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
244420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
244520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
244620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
244720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
244814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
244920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
245020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
245120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
245220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
245320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
245420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
245520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
245620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
245720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
24587a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
245920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
246020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
246120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2462a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2463a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = rowHandler.delete(mDb, c);
24648e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2465a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            if (rowHandler.isAggregationRequired()) {
2466a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                triggerAggregation(rawContactId);
2467a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            }
24688e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return count;
246920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
247020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
247120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
247220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
247320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
247420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2476ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2477f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2478f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2479f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2480f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2481f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
2482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
2483ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2484ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2485ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2486f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
248767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2488f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
248967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2490f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2491ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2492f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2493f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
249473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
249573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2496f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2497ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2498f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
24991a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2500ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2501ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2502ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2503ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2504ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
25055aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2506e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
25075aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
25081a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
25091a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2510e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
25111a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2512e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2513e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2514e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2515ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
251682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
25171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
251882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
251982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
25200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
25214dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
25224dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
25230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
252482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
25254dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
25264dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
25274dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
25284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
25291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
25301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2531dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2532dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
253382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2534f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
2535dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2536dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2537dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2538f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=");
2539f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(dataId);
25401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2541dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2542dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
25430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
25440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
25450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
25460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2547dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2548dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2549dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2550dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
2551f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2552f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2553f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2554f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2555f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2556f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2557f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
2558f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (")
2559f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2560f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(",")
2561f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2562f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(")" + " AND " + Data.DATA1 + "=");
2563f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2564f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND ((" + DataColumns.MIMETYPE_ID + "=")
2565f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2566f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2567f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol);
2568dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2569f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2570f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2571dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2572f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=")
2573f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2574f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append("))");
2575dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
2576f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=")
2577f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2578f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2579f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol)
2580f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.DATA + "=");
2581f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2582dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2583f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2584f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2585dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2586dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
25871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
258882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
2589f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=")
259082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                        .append(values.getAsLong(StatusUpdates.DATA_ID));
2591dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
259270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2593f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
259470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
25951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
25961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2597de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
2598c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    mSb.toString(), null, null, null,
2599c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
26001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
260167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
26025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2603e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
26041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
26051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
26061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
26071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
26081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
260931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
261031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
261131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
26121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
26131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
261482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2615a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2616a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2617a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2618a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2619a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2620a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2621a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
262282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2623a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2624a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
262582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
262682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
262782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
262882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
262982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2630a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
263182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
263282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
26331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2634a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2635a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2636a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2637e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
26380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
263982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
264082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
26410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
26420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
26430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
26450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
26460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
26470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
26480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
26490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
26510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
26520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2653a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
2654a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
2655a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
265682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
265782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
2658a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
2659a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
26600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 3, status);
26610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 4, resPackage);
26620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 5, iconResource);
26630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 6, labelResource);
2664a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
2665a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
2666a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2667a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
2668a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
26690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 2, status);
26700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 3, resPackage);
26710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 4, iconResource);
26720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 5, labelResource);
2673a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
2674a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
2675a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
26760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
2677a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
26780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 2, status);
2679a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
26800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 4, status);
2681a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
26820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 1, resPackage);
26840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 2, iconResource);
26850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 3, labelResource);
26860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
26870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
2688a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
2689e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2690e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2691bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2692a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2693a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
2694a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
2695a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
2696a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2697a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2698a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
26991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
27001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
27014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2702de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2703bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2704b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2705b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2706b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2707f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2708f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2709508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2710508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
271135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2712b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
271335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2714b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2715b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2716b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2717b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2718b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2719b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2720cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2721cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2722cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2723cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2724cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2725d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2726d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2727cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
27286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
27296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
27302e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
27312e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
27322e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
27332e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
27342e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
27352e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
27362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
27372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
27382e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
27392e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
27402e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
27412e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
27422971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
27432971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
27442971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2745e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
27472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
27482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2749f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        numDeletes += deleteRawContact(rawContactId, callerIsSyncAdapter);
27502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
27512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27532971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
27542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
27552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
27562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
27575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
27582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2759f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteRawContact(rawContactId, callerIsSyncAdapter);
2760508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2761508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
276220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2763f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2764944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2765f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
276620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
276720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
276848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
276948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
277048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
277148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2772508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2773f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
27744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
27754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
2776ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2777ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2778ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2779f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
27805aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
27812971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
27822971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
27832971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
27842971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
27852971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2786e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27872971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
27882971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
27895aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
27902971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
27912971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27922971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27932971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
279481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2795f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
279681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
27972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2798508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2799508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2800eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
280143880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
28025aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteSettings(uri, selection, selectionArgs);
2803eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2804eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
280582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
28060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
28071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
28081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
280981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
281081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
28113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
281281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2813508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
28144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
28154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
28161c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2817ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2818b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
281994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2820de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
282194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
282294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
282394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
282494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2825f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2826de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
282794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
282894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
282994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2830f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2831de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
283294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
283394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
28341a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
283594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
283694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
283794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
28385aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2839e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
28401a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2841e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2842e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2843e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2844cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
2845cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2846cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
2847cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2848cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2849cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2850cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
2851cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2852cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2853cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2854cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2855cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2856cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2857cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2858cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2859f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    public int deleteRawContact(long rawContactId, boolean callerIsSyncAdapter) {
28603389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
2861f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
286214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2863de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
286433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2865b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2866cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
286733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
286833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
286933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
28700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
28719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
28729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
28739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
28749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
28759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
28769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
28779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
28789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
28790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
28800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2881cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
288281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
288381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2884cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2885cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2886cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2887cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2888cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2889cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2890cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
2891cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2892cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
28934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2894de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2895de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2896bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2897b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2898b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2899b5a4add17815167d20a90645779df34cdf45280dFred Quintana
290035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
290100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
290200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2903b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2904b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
29051129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2906b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
2907b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2908b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2909b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2910f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2911f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
291200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
291335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2914b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2915b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2916b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2917b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2918b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2919b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2920b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2921b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2922b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2923b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2924b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
292535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2926d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
29278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
292800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
292900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
293000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2931d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
29328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
2933c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2934c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2935c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
29362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
29372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
29382e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
29392e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
29402e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
29412e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
29422e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
29432e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
29442e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
29458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
29462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
29472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
29482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
29497d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
29507d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
29517d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
29527d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
29537d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
29547d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
29557d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
29567d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
29577d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
29587d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
295920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2960944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2961f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
296281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2963f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
296481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
296520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
296620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2967c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
296848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
296948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
297048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
297148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2972f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
297381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2974f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
297581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
297600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
297700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
29787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
29795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
29805ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
29814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
29827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
29837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
29847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
29855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
298633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
29874529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
29884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
29894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
29904529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
29914529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
29924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
29934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1);
29944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
29957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
29967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
29977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2998ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
29995aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3000f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
300181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3002f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
300381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3004ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3005ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3006ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3007ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3008ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
30094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
30104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
301173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
30125aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
30135aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
301481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3015f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
301681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3017ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3018ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3019ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3020127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3021de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3022b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3023b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3024b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3025eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
30265aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateSettings(uri, values, selection, selectionArgs);
302743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3028eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3029eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3030eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
30319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
30329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
30339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
30349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
30359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
303681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
303781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3038f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
303981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
304000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
304100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
304200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
30434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
30444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
30459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
30469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
30479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
30489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
30499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
30509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
30519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
30529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
30539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
30549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
30559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
30569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
30579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
30599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
30609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
30619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
30629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
30639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
30649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
30659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
30669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
30679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
30709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
30719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
30729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
30739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
30749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
30759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
30769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
30779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
30789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
30819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
30829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
30839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
30849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
30859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
30869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
30879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
30889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
30899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
30909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
30919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
30929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
30939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
30969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
30979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
30989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
30999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
31009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
31019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
31025aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3103f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
310473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3105ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3106ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
310773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3108f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
310973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
311073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
311173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
311273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
311373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
311473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
311573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
311673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3117ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
31181a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
31191a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
312094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
31216ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
31221129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
31236ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            final long groupId = ContentUris.parseId(uri);
31244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(groupId);
31256ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
31264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Groups.ACCOUNT_TYPE}, Groups._ID + "=?", mSelectionArgs1, null,
31276ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
31286ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
31296ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
31306ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
31316ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
31326ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
31336ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
31346ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
31356ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3136ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
31376ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
31386ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
31396ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
31406ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
31416ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
31426ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
31436ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
31446ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
314594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
314694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
314794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3148b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3149b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3150e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
31511a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31521a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3153e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3154e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3155e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3156e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
31574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
31584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
31594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
31604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
31614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
316273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
31634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3164b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
316551bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
31664529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
31674529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
31684529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
31694529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
31704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
31714529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
31724529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
31734529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
31744529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
31754529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
31764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
31774529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
31784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
31794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
31804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
318119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final String selection = RawContacts._ID + " = " + rawContactId;
318219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
318319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
318419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3185ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3186ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
318719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
318819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
318919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    null, null, null, null);
319019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
319119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
319219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3193ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3194ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
319519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
319619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
319719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
319819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
319919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
320019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
320119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
320219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, null);
32035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3204433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
32054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3206433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3207285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
3208285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateLookupKey(mDb, rawContactId);
3209285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
321019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
321119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
3212ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
321319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
32145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
32155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
321633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
321733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3218321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3219f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
322020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
322120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
322220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
32235ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
322420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
322520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
322620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
322720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
322820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3229b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
323020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
323120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
323270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
323370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
323420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
323520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
323620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
323770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
323820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
323970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
324020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
324170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
324220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
324370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
324420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
324520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3246653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
324720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3248653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3249653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
325014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3251653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3252653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3253f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
325420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3255653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3256653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
325720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
325820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3259653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
326020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
326120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3262f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3263653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3264653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3265321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3266653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
326714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3268a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3269f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
32708e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
3271a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
3272a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
3273a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
32748e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
3275653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return 1;
3276321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3277321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
32788c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
32798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
32808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3281b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
32828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
32838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
32848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
32858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
32868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
32878c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
32888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
32898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
32908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
32918c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
32928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
32938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
32948c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
32958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
32968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
32978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3298d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
32998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3300b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3301d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3302b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3303d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3304b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3305d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3306b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3307d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3308b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3309d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3310d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3311d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
33128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3313d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3314d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3315d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
33168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3317c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
33188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3319c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3320c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
33214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
33224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?", mSelectionArgs1);
33238c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
33258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
33268c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3327b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
33288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3329b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
33308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3331b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
33328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3333b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
33348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3335b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
33368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
33378c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33384da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
3339f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3340d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3341ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    public void updateContactLastContactedTime(long contactId, long lastTimeContacted) {
3342ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
3343ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(2, contactId);
3344ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.execute();
3345d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
3346d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3347127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3348127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
33490c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
33500c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
335180c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
33520c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
33530c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
33540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
33550c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
33560c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
33570c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
33580c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3359b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3360127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
33610c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
33624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
33634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
33640c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
33654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
33664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
33670c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
33686bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
33696bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
33700c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
33710c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
33720c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
33730c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3374127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3375127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
33763389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3377dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
3378dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
3379dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3380b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
33810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
33820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
3383b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
33840c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
3385127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3386127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3387127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3388127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3389b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3390b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
339170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3392b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
339370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        if (mDb == null) return;
339470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
3395627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
3396627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        boolean hasUnassignedContacts[] = new boolean[]{false};
339770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
339870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3399627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3400627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.RAW_CONTACTS, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE);
3401627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3402627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.GROUPS, Groups.ACCOUNT_NAME, Groups.ACCOUNT_TYPE);
3403627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3404627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE);
340548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3406627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3407627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // in the existingAccounts set will be extra accounts whose data must be deleted.
3408627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3409627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3410627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
341170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
341270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
341370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
34145f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
3415627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                String[] params = new String[] {account.name, account.type};
3416627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3417627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.GROUPS +
3418627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3419627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3420627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3421627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.PRESENCE +
3422627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3423627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                "SELECT " + RawContacts._ID +
3424627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
3425627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3426627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3427627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3428627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.RAW_CONTACTS +
3429627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3430627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3431627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3432627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.SETTINGS +
3433627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3434627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3435627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3436627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3437627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            if (hasUnassignedContacts[0]) {
3438627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3439627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                Account primaryAccount = null;
3440627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                for (Account account : accounts) {
3441627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    if (isWritableAccount(account)) {
3442627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        primaryAccount = account;
3443627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        break;
3444627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    }
3445627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3446627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3447627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (primaryAccount != null) {
3448627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    String[] params = new String[] {primaryAccount.name, primaryAccount.type};
3449627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3450627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3451627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.RAW_CONTACTS +
3452627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + RawContacts.ACCOUNT_NAME + "=?,"
3453627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + RawContacts.ACCOUNT_TYPE + "=?" +
3454627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
3455627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL", params);
3456627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3457627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // We don't currently support groups for unsynced accounts, so this is for
3458627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // the future
3459627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3460627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.GROUPS +
3461627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + Groups.ACCOUNT_NAME + "=?,"
3462627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + Groups.ACCOUNT_TYPE + "=?" +
3463627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " IS NULL" +
3464627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + Groups.ACCOUNT_TYPE + " IS NULL", params);
3465627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
346670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
3467627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3468b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
346970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
347070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
347170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
347270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
347370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3474619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3475619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3476627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3477627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3478627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    private void findValidAccounts(Set<Account> validAccounts, boolean[] hasUnassignedContacts,
3479627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            String table, String accountNameColumn, String accountTypeColumn) {
3480627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        Cursor c = mDb.rawQuery("SELECT DISTINCT " + accountNameColumn + "," + accountTypeColumn
3481627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                + " FROM " + table, null);
3482627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3483627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3484627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (c.isNull(0) && c.isNull(1)) {
3485627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    hasUnassignedContacts[0] = true;
3486627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                } else {
3487627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3488627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3489627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3490627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3491627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3492627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3493627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3494627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3495627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
3496622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
3497622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
349867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
349967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
350067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
350167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
350267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
350367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
350467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
350567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
350667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
350767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
350867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
350967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
3510dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
3511622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
3512dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
3513dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
3514622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
3515622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
3516dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
3517622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
3518622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
35194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
35204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
35214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
3522bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3523bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3524bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
35250b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3526b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
352735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3528d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
35291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3530c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3531c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3532619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3533619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3534a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
35354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
353635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3537b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
353835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
353935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3540d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3541763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3542619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3543619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3544619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3545d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
35464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3547763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
35484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
35494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
35506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
35516bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35526bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
35545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
35555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
35565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
35575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
35585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
35595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
35615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
35625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
35635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3564763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
35654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    String[] args;
35664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    if (selectionArgs == null) {
35674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[2];
35684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    } else {
35694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[selectionArgs.length + 2];
35704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
35714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    }
35724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    args[0] = String.valueOf(contactId);
35734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    args[1] = lookupKey;
35744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
35754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
35765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
35775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
35785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
35795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
35805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
35825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3584763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
35854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
35864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
35874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
35885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
35895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3591f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3592f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
3593f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
3594763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3595f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
35964da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
35974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
35984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
3599f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3600f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3601f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
3602ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3603763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3604ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
36054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3607e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
36094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sb.toString());
3610ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3611ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3612ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3613ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3614ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3615ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
36164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3617ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3618d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
36194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3621e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
36225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
36234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
36244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
36254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3626763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3627ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
36285e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
36295e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
36305e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
36315e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36325e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36335e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
36345e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
36354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
36364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
36374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3638d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
36395e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
36405e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
36414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3642d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3643d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3644d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3645763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
36464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
36474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3648d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
36495e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
36505e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3651d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3652d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
36534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3654d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3655d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3656d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3657d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
36584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
36594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3660d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3661d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3662d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3663d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3664d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3665d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3666ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3667763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3668b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
366971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
36704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3671b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3672b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3673b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3674b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3675d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
36764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
367782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
36794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
36806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
36816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
368200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3683ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
36843653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
368582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
36874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
36883653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
36893653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
36903653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
36913653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
36924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
369382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
369489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
36952815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
36962815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
36972815f58f72f109790585931f601a63ddc02536a5Evan Millar
369848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
369982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
370148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
37024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
370348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
370448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
370548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3706ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
370782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
370889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3709ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
37104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
37114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3712a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
37135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
37145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
37155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
37165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
37175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
371820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
37195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
37205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
37215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
37225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
37235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
37245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
37255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
37265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
37275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
37285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
37295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
37305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
37315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
37325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
37335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
37345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
37355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3736a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
3737ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
37385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3739a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3740a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3741a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3742ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3743ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3744ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
37454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
374682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
374789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
37484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
37494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
37504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
375148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
375282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
37534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
37554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
375648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
375748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
375848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
37595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
376082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
376189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
37624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
37634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    qb.appendWhere(" AND " + Email.DATA + "=?");
37654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3766ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3767ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3768ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
37695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
377082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
377107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
377207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
377307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
377407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
377507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
377607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
377707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
37785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
377907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
378007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
378107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
378207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
378307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
378407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
378507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
378607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
378707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
378807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
378907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " AND " + Data.DATA1 + " LIKE ");
379007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
379120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
379220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
379320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
379407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
379507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
379607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
379707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
379807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
379907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
380007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
380107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
380207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
380307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
380407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
380507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
380607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
380707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " AND " + Data.RAW_CONTACT_ID + " IN ");
380820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
380920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
38105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
38115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3812a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
38135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
38145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3815a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3816a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3817a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
38185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
38195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
38205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3821ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
382282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
382389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
382489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3825ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3826ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3827ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
382848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
382982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
383148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
383248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
38334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
383448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
383548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
383648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
38375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
3838763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
38394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
38404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
38414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38425ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
38435ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
3844763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
38454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
38464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
38474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
38484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
38494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
38515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
385282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
38544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
3855e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3856e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3857e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3858e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
385982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3860e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3861e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3862e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
38634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
386482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
38654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
3867a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3868a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3869a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3870a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
38714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3872a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3873a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3874a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3875e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
3876a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3877a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3878e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3879b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
3880e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3881e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
3882e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3883e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3884e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3885a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3886a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3887a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3888ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
3889b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3890ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
389189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3892ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3893ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3894ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3895ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3896b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3897ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
38984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
38994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
3900ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3901ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3902ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3903ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
3904b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3905ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
390689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
390789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3908ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3909ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3910ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3911b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
39120c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3913b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3914b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3915b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3916b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
391731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3918d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
39192d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
39202d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
39212d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
39222d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
392331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3924d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3925d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
392631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
392731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
392831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
392931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3930763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
39317581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
39327581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
39332d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
393431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
393531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3936eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3937eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3938eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
393989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3940e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3941e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3942e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3943b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3944e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
394582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3946b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3947e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3948e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
394982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3950b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3951e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3952e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3953e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3954eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3955eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3956eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
395782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
39580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
39595ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
39605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
39615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
396282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
39630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
39644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
39654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
39665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
39675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
39685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3969c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3970a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3971c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3972c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3973c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
3974b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3975b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
3976c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3977c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
39781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
3979b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39801b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
39811b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
39821b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
39831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
3984b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
39861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
39871b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
39881b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
39891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
3990b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
39921b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
39931b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
39941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
39951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
3996b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
399871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
39991b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
40001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
40011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
400246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
400346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
400446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
400546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
400646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
400746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
400846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
400946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
40104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
40114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
401246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
401346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
401446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
40154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4016f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4017c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
40184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
40194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
40215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
40245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
40255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4026038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4027038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4028038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4029038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
40305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
40315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
40324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
40334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
40344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
40354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
40364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
40374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
40395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
40405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
40415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
40435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (contactId == -1) {
40445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
40455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
40485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
40515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
40525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
40545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
40555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
40565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
40575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
40585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
40595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
40615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
40625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
40635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
40645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
40675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
40685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int sourceIdCount = 0;
40695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
40705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
40715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
40725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sourceIdCount++;
40735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (sourceIdCount == 0) {
40775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
40785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
40815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
40825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
40835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
40845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
40855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
40865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
40875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
40885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
40915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
40925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
40945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
40955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
40965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
40975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
40985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
40995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
41025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
41035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
41045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
41055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
41065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
41075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
41085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
41125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
41135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
41165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupByDisplayNameQuery {
41195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
41205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
41225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
41235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
41245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
41255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
41265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
41275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
41295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
41305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
41315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int NORMALIZED_NAME = 3;
41325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
41355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
41365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int displayNameCount = 0;
41375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
41405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                displayNameCount++;
41415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
41455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
41465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
41495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
41505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
41515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
41525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
41545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
41555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
41565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
41595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
41605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
41615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
41635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
41645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
41655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
41665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
41675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
41685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
41715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
41725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
41735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
41745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
41755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
41765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
41775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
41815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
41825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
41855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
41885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
41895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
41905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
41915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
41925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
41945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
41955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
41975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
41985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
42005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
42015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
42025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
42035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
42045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
42055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
42065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
42075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
42085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
42095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
42115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
42125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
42165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
42175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
42185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
42195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
42205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
42215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4222763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4223763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
422482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4225763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4226f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4227763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4228763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4229d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4230763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4231763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
4232b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
423382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
423482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
423582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
423682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
4237b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
423882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
423982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
424082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
424182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
424282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
42433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
42443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
4245a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
42463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
424782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
424882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
424982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
425082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
4251ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
4252763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4253763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
4254763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4255f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4256763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4257763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4258d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4259763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4260763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
4261763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4262763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4263763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4264763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4265763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
426646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
426746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        // Note: currently, "export only" equals to "restricted", but may not in the future.
426846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
426946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.FOR_EXPORT_ONLY, false);
427046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
4271f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
427246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
427346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        if (requestingPackage != null) {
427446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            excludeRestrictedData = excludeRestrictedData
427546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
427646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        }
427746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
427846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setProjectionMap(sRawContactsEntityProjectionMap);
427946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
428046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
428146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
428282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
428382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
428482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4285d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa        // Note: currently, "export only" equals to "restricted", but may not in the future.
4286763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
4287d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa                Data.FOR_EXPORT_ONLY, false);
4288763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4289f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4290763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4291763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4292763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            excludeRestrictedData = excludeRestrictedData
4293d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4294763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4295763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4296763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getDataView(excludeRestrictedData));
429782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
429882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
42993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated presence when requested
4300b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
430182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
43023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
430382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
430482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
430582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
43063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated status updates when requested
4307b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
430882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS,
430982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
431082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
431182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
431282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
43133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
43143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
431582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
43163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
4317ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
43183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
43193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual presence when requested
43203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
43213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
43223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdates.DATA_ID + "="
43233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + DataColumns.CONCRETE_ID + ")");
43243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
43253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
43263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual status updates when requested
43273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection,
43283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS,
43293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_RES_PACKAGE,
43303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_ICON,
43313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_LABEL,
43323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_TIMESTAMP)) {
43333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
43343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
43353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + DataColumns.CONCRETE_ID + ")");
43363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
43373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
433882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
433982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
434082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4341ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4342ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
43430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
43440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
43450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4346b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
43470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
43480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4349b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
43500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
43510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
43520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
43530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
43540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4355b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
43560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
43570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
43580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
43590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
43600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
43610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
43620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
43630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
43640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
43650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setTables(sb.toString());
43660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
43670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
43680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
43694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
4370f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4371f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
43724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
43734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
43744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
43754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
43764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
43774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
43784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
43794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
43804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
43814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4382e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
4383f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4384f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
4385e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        if (!TextUtils.isEmpty(accountName)) {
4386e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
4387e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4388e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
4389e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
4390e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
4391e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
4392e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
4393e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
4394e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
4395e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
4396e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
4397e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
4398e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
4399e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
4400e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
44017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
4402c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4403c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
4404c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4405c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4406c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
4407f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
4408f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String limitParam = getQueryParameter(uri, "limit");
4409c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
4410c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4411c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4412c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
4413c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
4414c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
4415c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
4416c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
4417c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
4418c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4419c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
4420c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
4421c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
4422c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4423c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4424c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4425c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
44265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
44275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
44285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
44295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
44305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
44315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
44325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
44345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
44355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
44375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
44405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
44415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
44445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
44475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
44485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
44495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
44515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
44525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
44545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
44555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
445600ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
4457d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
445870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
445970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
44606cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
446170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
446270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
446370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
446470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
4465d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
446670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
446767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
44685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
44695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4470619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
4471619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
4472619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4473b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
4474b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
4475b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
4476b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
4477d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
4478b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                if (!"r".equals(mode)) {
4479b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
4480b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
4481b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4482b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                String sql =
4483b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
4484b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
44854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=?";
4486b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
44874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql,
44884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
4489d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4490d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4491f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4492d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
4493d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
449414b8a1243ab5c043b35e47527ca1c962064f3771Daisuke Miyakawa                final String selection = Contacts._ID + "=" + contactId;
4495d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4496d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
4497d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
4498d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
4499d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
4500d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                outputRawContactsAsVCard(localStream, selection, null);
4501d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
4502d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4503b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4504b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
4505b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new FileNotFoundException("No file at: " + uri);
4506b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
4507b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
4508b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4509d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
4510d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String VCARD_TYPE_DEFAULT = "default";
4511d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4512d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4513d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
4514d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
4515d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4516d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
4517d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
4518d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
4519d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
4520d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4521d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
4522d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
4523d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4524d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
4525d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
4526d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
4527b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4528d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
4529d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
4530d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
4531d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4532d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
4533d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4534d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4535d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4536d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
4537d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
4538d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
4539d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4540d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
4541d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
4542d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
4543d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
4544d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
4545d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4546f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
4547d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        if (!composer.init(selection, selectionArgs))
4548d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
4549d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4550d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
4551d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
4552d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
4553d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4554d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4555d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
4556d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4557b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4558619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
45597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
45607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
45617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
4562d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static class RawContactsEntityIterator implements EntityIterator {
45637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
45647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
45657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
45677a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
45687a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
45697a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
45707a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
45717a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
45727a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
45737a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
45747a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
45757a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
45767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
45777a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
45787a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
45797a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
45807a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
45817a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
45827a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
45837a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
45847a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
45857a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4};
45867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
458746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public static final String[] PROJECTION = new String[]{
45886cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
45896cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
45906cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.SOURCE_ID,
45916cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.VERSION,
45926cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.DIRTY,
459346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts.Entity.DATA_ID,
45947a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RES_PACKAGE,
45957a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.MIMETYPE,
45967a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
45977a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
45987a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
45997a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
46007a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
46017a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
46027a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
46037a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
46047a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
46057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
46067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
46077a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
46087a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
46097a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
46107a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
46117a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
46127a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
46137a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
46147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4,
461546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts._ID,
46167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.IS_PRIMARY,
46173cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar                Data.IS_SUPER_PRIMARY,
46187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA_VERSION,
46197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                GroupMembership.GROUP_SOURCE_ID,
46207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC1,
46217a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC2,
46227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC3,
462394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                RawContacts.SYNC4,
462438446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey                RawContacts.DELETED,
4625c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.CONTACT_ID,
4626bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey                RawContacts.STARRED,
4627bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey                RawContacts.IS_RESTRICTED};
4628035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
4629035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
4630035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
4631035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
4632035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
4633035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
4634035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
463567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
463667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
463767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
46387a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_RAW_CONTACT_ID = 27;
46397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_IS_PRIMARY = 28;
46403cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_IS_SUPER_PRIMARY = 29;
46413cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DATA_VERSION = 30;
46423cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_GROUP_SOURCE_ID = 31;
46433cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC1 = 32;
46443cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC2 = 33;
46453cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC3 = 34;
46463cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC4 = 35;
46473cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DELETED = 36;
46483cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_CONTACT_ID = 37;
46493cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_STARRED = 38;
4650bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey        private static final int COLUMN_IS_RESTRICTED = 39;
46517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
465246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public RawContactsEntityIterator(ContactsProvider2 provider, Uri entityUri,
465346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String contactsIdString,
465446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String selection, String[] selectionArgs, String sortOrder) {
46557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
465646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            Uri uri;
46577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
465846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, contactsIdString);
465946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(uri, RawContacts.Entity.CONTENT_DIRECTORY);
466046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            } else {
466146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = ContactsContract.RawContactsEntity.CONTENT_URI;
4662035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
466346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            final Uri.Builder builder = uri.buildUpon();
466446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            String query = entityUri.getQuery();
466546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            builder.encodedQuery(query);
466646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            mEntityCursor = provider.query(builder.build(),
466746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    PROJECTION, selection, selectionArgs, sortOrder);
46687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
46697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
46707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4671038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4672038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4673038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4674038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4675038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4676038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4677038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana
46787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
46797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
46807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
46817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
46827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
46837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
46847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
46857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
46877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
46887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
46897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
46907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
46927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
46937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
46957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
46967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
46977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
46987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
46997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
47007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
47017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
47027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
47037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
47047a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
47057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
47067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
47077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
47086cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
47096cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
47105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
47116cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
47126cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
47136cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
47147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
47157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
47167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
47177a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
471894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            contactValues.put(RawContacts.DELETED, c.getLong(COLUMN_DELETED));
471938446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey            contactValues.put(RawContacts.CONTACT_ID, c.getLong(COLUMN_CONTACT_ID));
4720c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            contactValues.put(RawContacts.STARRED, c.getLong(COLUMN_STARRED));
4721bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey            contactValues.put(RawContacts.IS_RESTRICTED, c.getInt(COLUMN_IS_RESTRICTED));
47227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
47237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
47247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
47257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
47267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
47277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
47287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
472923c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                if (c.isNull(COLUMN_CONTACT_ID)) {
473023c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                    continue;
473123c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                }
47327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
47337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
473423c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data._ID, c.getLong(COLUMN_DATA_ID));
47357a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
47367a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
473723c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_PRIMARY, c.getLong(COLUMN_IS_PRIMARY));
473823c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_SUPER_PRIMARY, c.getLong(COLUMN_IS_SUPER_PRIMARY));
47397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
47409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
47419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
47429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
47439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
47447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
47457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                for (int i = 0; i < DATA_KEYS.length; i++) {
47467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
47477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
47487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
47497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
47507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
47517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
47527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
47537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
47547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
47557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
47567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
47577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
47587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
47597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
47607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
47617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
47627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
47637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
47647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
47657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
47667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4767226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    /**
4768226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
4769226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
4770226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     */
4771226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    private static class GroupsEntityIterator implements EntityIterator {
4772226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private final Cursor mEntityCursor;
4773226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private volatile boolean mIsClosed;
4774226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4775226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final String[] PROJECTION = new String[]{
4776226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups._ID,
4777226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_NAME,
4778226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_TYPE,
4779226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.SOURCE_ID,
4780226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.DIRTY,
4781226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.VERSION,
4782226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.RES_PACKAGE,
4783226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE,
4784226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE_RES,
47857a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.GROUP_VISIBLE,
47867a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC1,
47877a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC2,
47887a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC3,
47897a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC4,
47907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYSTEM_ID,
479194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.NOTES,
47921a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.DELETED,
47931a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.SHOULD_SYNC};
4794226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4795226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ID = 0;
4796226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_NAME = 1;
4797226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 2;
4798226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_SOURCE_ID = 3;
4799226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_DIRTY = 4;
4800226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_VERSION = 5;
4801226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_RES_PACKAGE = 6;
4802226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE = 7;
4803226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE_RES = 8;
4804226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_GROUP_VISIBLE = 9;
48057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 10;
48067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 11;
48077a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 12;
48087a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 13;
48097a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYSTEM_ID = 14;
48107a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_NOTES = 15;
481194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        private static final int COLUMN_DELETED = 16;
48121a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        private static final int COLUMN_SHOULD_SYNC = 17;
4813226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4814226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
4815226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
4816226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = false;
4817226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4818226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String updatedSortOrder = (sortOrder == null)
4819226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    ? Groups._ID
4820226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    : (Groups._ID + "," + sortOrder);
4821226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4822b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            final SQLiteDatabase db = provider.mDbHelper.getReadableDatabase();
4823226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4824b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            qb.setTables(provider.mDbHelper.getGroupView());
4825226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setProjectionMap(sGroupsProjectionMap);
4826226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (groupIdString != null) {
4827226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups._ID + "=" + groupIdString);
4828226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4829f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            final String accountName = getQueryParameter(uri, Groups.ACCOUNT_NAME);
4830f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            final String accountType = getQueryParameter(uri, Groups.ACCOUNT_TYPE);
4831226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!TextUtils.isEmpty(accountName)) {
4832226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups.ACCOUNT_NAME + "="
4833226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4834226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + Groups.ACCOUNT_TYPE + "="
4835226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
4836226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4837226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
4838226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    null, null, updatedSortOrder);
4839226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToFirst();
4840226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4841226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4842226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public void close() {
4843226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4844226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("closing when already closed");
4845226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4846226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = true;
4847226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.close();
4848226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4849226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4850226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public boolean hasNext() throws RemoteException {
4851226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4852226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
4853226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4854226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4855226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return !mEntityCursor.isAfterLast();
4856226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4857226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4858038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4859038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4860038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4861038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4862038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4863038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4864e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4865226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public Entity next() throws RemoteException {
4866226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4867226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
4868226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4869226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!hasNext()) {
4870226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
4871226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4872226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4873226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
4874226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4875226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final long groupId = c.getLong(COLUMN_ID);
4876226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4877226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            // we expect the cursor is already at the row we need to read from
4878226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            ContentValues groupValues = new ContentValues();
4879226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4880226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4881226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups._ID, groupId);
4882226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
4883226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
4884226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4885226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4886226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
4887226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
4888226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
48897a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
48907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
48917a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
48927a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
48937a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
489494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            groupValues.put(Groups.DELETED, c.getLong(COLUMN_DELETED));
48957a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
48961a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            groupValues.put(Groups.SHOULD_SYNC, c.getString(COLUMN_SHOULD_SYNC));
4897226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            Entity group = new Entity(groupValues);
4898226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4899226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToNext();
4900226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4901226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return group;
4902226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4903226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    }
4904226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4905a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
49067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
49077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
4908568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
4909568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
49107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
49117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
49125ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS:
49135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID:
49147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
49155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (match == RAW_CONTACTS_ID) {
49167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
49177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
49187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
491946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                return new RawContactsEntityIterator(this, uri, contactsIdString,
492046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                        selection, selectionArgs, sortOrder);
4921226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS:
4922226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS_ID:
4923226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String idString = null;
4924226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                if (match == GROUPS_ID) {
4925226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    idString = uri.getPathSegments().get(1);
4926226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                }
4927226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4928226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                return new GroupsEntityIterator(this, idString,
4929226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        uri, selection, selectionArgs, sortOrder);
49307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
49317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
49327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
49337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
49347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
49354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
49364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4937a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
49384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4939b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4940b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP:
4941be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
4942b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4943b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4944b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4945f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
4946f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4947b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4948be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4949b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4950b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4951508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4952b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
495348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
495448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
495548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
495648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
495748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
495848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
495948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
496048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
496148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
496248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
496348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
496448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4965b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4966b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4967b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4968b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4969b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4970b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4971b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4972b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4973c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4974c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4975c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4976c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
497761efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
497861efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
49794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
49804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
49817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
498225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
49833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
498425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
49853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
498625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindNull(1);
49873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
498825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
498925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
499025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
49913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
49923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
499373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
499473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
499573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
499673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
4997a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
499873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
499973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
5000c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
5001c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
5002c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
5003c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
5004c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
5005c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
5006653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
5007c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
5008653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
5009653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
5010c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
5011c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
5012c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
5013c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
5014c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
5015c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
5016c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
5017c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
5018c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
5019653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
5020c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
5021653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
5022653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
5023c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
5024c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
5025ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5026f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
5027f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
5028f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
5029f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5030f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5031f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
5032f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokens.length == 0) {
5033f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
5034f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5035f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5036f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String address = tokens[0].getAddress();
5037f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int at = address.indexOf('@');
5038f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (at != -1) {
5039f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            address = address.substring(0, at);
5040f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5041f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5042f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
5043f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
5044f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5045f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5046f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5047f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
5048f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5049f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
5050f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
5051f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
5052f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5053f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5054f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
5055f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
5056f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5057f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5058a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
5059a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
5060a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
5061a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
5062a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
5063a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
5064a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
5065a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
5066a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
5067a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
5068a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
5069f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5070f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
5071f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
5072f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5073f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5074315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private interface NicknameLookupPreloadQuery {
5075315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
5076315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5077315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String[] COLUMNS = new String[] {
5078315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            NicknameLookupColumns.NAME
5079315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        };
5080315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5081315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int NAME = 0;
5082315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
5083315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5084315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    /**
5085315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * Read all known common nicknames from the database and populate a Bloom
5086315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * filter using the corresponding hash codes. The idea is to eliminate most
5087315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * of unnecessary database lookups for nicknames. Given a name, we will take
50883684089aba82df3f7a0c111e7c96ed8b0380e57aDmitri Plotnikov     * its hash code and see if it is set in the Bloom filter. If not, we will know
5089315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * that the name is not in the database. If it is, we still need to run a
5090315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * query.
5091315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * <p>
5092315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * Given the size of the filter and the expected size of the nickname table,
5093315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * we should expect the combination of the Bloom filter and cache will
5094315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * prevent around 98-99% of unnecessary queries from running.
5095315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     */
5096315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private void preloadNicknameBloomFilter() {
5097315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        mNicknameBloomFilter = new BitSet(NICKNAME_BLOOM_FILTER_SIZE + 1);
5098315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5099315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupPreloadQuery.TABLE,
5100315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                NicknameLookupPreloadQuery.COLUMNS,
5101315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                null, null, null, null, null);
5102315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        try {
5103315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            int count = cursor.getCount();
5104315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            for (int i = 0; i < count; i++) {
5105315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                cursor.moveToNext();
5106315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                String normalizedName = cursor.getString(NicknameLookupPreloadQuery.NAME);
5107315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                int hashCode = normalizedName.hashCode();
5108315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                mNicknameBloomFilter.set(hashCode & NICKNAME_BLOOM_FILTER_SIZE);
5109315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            }
5110315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        } finally {
5111315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            cursor.close();
5112315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        }
5113315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
5114315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5115315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5116f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5117f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
5118f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5119f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
5120315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int hashCode = normalizedName.hashCode();
5121315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        if (!mNicknameBloomFilter.get(hashCode & NICKNAME_BLOOM_FILTER_SIZE)) {
5122315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            return null;
5123315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        }
5124315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5125f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SoftReference<String[]> ref;
5126f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
5127f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        synchronized (mNicknameClusterCache) {
5128f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
5129f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
5130f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                if (ref == null) {
5131f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    return null;
5132f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
5133f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = ref.get();
5134f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
5135f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5136f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5137f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (clusters == null) {
5138f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
5139f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
5140f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
5141f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
5142f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
5143f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5144f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
5145f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5146f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5147315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private interface NicknameLookupQuery {
5148315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
5149315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5150315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String[] COLUMNS = new String[] {
5151315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
5152315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        };
5153315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5154315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int CLUSTER = 0;
5155315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
5156315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5157f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
5158b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5159f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
5160f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
5161f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
5162f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                null, null, null);
5163f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
5164f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
5165f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (count > 0) {
5166f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = new String[count];
5167f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int i = 0; i < count; i++) {
5168f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    cursor.moveToNext();
5169f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
5170f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
5171f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
5172f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        } finally {
5173f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            cursor.close();
5174f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5175f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
5176f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5177f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5178f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
5179f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5180f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
5181f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
5182f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5183f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5184f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5185f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5186f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
5187f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
5188f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5189f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5190f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5191f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5192f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
5193f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5194f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5195f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5196f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5197f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
5198f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5199f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
5200f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
5201f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
5202f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
5203f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
5204f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
5205f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5206f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5207f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5208f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
5209f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5210f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
5211f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
5212f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
5213f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5214f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
52152d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5216d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5217d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5218d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5219d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
5220d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5221d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5222d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5223e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5224d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN("
5225d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
5226d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
5227d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NICKNAME + ","
5228d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.ORGANIZATION + "))");
5229e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5230e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
52315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
5232c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5233c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
5234c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
5235c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5236c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5237a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
5238c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            String limit) {
523920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), limit,
524020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                true);
52415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
52425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
52435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
524420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            String limit, boolean allowEmailMatch) {
5245d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5246dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
5247d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
5248d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
5249d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
52505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
5251a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
5252a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
5253a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
525420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                + NameLookupType.ORGANIZATION);
525520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
525620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
525720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
525820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        sb.append(")");
52593de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
5260c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limit != null) {
5261c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
5262c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5263c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(")");
5264ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
5265ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
52664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
52674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
52684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
52694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5270b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5271b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5272b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5273b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5274b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
52754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
52764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5277b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5278b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5279b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5280caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
52815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
52825e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
52835e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
52845e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
52855e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
52865e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
52875e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
52885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
52895e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
52905e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
52915e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5292caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5293caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5294caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
5295df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
5296df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
5297caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5298caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5299caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5300caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
53016f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5302caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
53036f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5304caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5305f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5306627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    protected boolean isWritableAccount(Account account) {
5307627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5308627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5309627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5310627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
5311627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        account.type.equals(sync.accountType)) {
5312627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    return sync.supportsUploading();
5313627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5314627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5315627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5316627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5317627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
5318627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        return false;
5319627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5320b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5321f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5322f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5323f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5324f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5325f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5326f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5327f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5328f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5329f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5330f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5331f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5332f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5333f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5334f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5335f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5336f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5337f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5338f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5339f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5340f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5341f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5342f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5343f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5344f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5345f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5346f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5347f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5348f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5349f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5350f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5351f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5352f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5353f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5354f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5355f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5356f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5357f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5358f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5359f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5360f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5361f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5362f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5363f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5364f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5365f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5366f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5367f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5368f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5369f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5370f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5371f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5372f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5373f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5374f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5375f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5376f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5377f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5378f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5379f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5380f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5381f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5382f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5383f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5384f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5385f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5386f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5387f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5388f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
53894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5390