ContactsProvider2.java revision b4e61e064b59e8076df81b061add9fb358fd2ed9
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);
57746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
57846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
57946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
58046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
58146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
58246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.MIMETYPE, Data.MIMETYPE);
58346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA1, Data.DATA1);
58446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA2, Data.DATA2);
58546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA3, Data.DATA3);
58646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA4, Data.DATA4);
58746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA5, Data.DATA5);
58846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA6, Data.DATA6);
58946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA7, Data.DATA7);
59046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA8, Data.DATA8);
59146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA9, Data.DATA9);
59246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA10, Data.DATA10);
59346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA11, Data.DATA11);
59446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA12, Data.DATA12);
59546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA13, Data.DATA13);
59646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA14, Data.DATA14);
59746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA15, Data.DATA15);
59846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC1, Data.SYNC1);
59946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC2, Data.SYNC2);
60046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC3, Data.SYNC3);
60146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC4, Data.SYNC4);
60246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
60346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.STARRED, Data.STARRED);
60446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
60546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
60646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
60746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
60846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        sRawContactsEntityProjectionMap = columns;
60946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
6103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
6123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
6143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
6223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
6233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
6253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.PRESENCE,
6263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
6273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS,
6283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
6293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
6303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
6323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
6343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_ICON,
6363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
6373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
6395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
6405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
6415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
6425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
6435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
6445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
6455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
6475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
6485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
6495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
6505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
6515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
6525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
6535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
6545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
6555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
6565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
6575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
6585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
6595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
6605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
6615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
6625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
6635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
6645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
6655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
6665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
6678f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
6685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
6705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
6715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
6725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
6735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
6745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
675a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
6765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
6775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
6785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
6793296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6803296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
6813296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6823296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
6833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6843296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6853296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6863296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6893296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6903296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
6913296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
6923296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6933296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
6943296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
6953296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
6963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS,
6973296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
6983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
6993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7003296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
7013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
7033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
7053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
7063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
707e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
708e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
709e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
71056d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
71156d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar                Contacts.LOOKUP_KEY + " AS " + PhoneLookup.LOOKUP_KEY);
712e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
713e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
714e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
715e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
716e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
717e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
718e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
719e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
720e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
721e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
722e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
723e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
724e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
725e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
726e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
727e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
728e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
729e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
730e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
731e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
732e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
733e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
734e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
735e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
736e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
737e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
7389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
739ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
74189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
742035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
743035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
7449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
7459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
7469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
74789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
748ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
74967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
750ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
7513cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
75294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
7533cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
75438446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
75589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
75689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
75789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
75889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
759ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
760ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
7616cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
762ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
763ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
764d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
765d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
767ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
768ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
769d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
770d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
771ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
772f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
773ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
774ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
775b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
776b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
777b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
778b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
7790c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
7800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
781b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
782b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
783eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
784eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
785eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
786eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
787eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
788eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
789341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
790341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
791341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
792fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
793341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
794341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
795341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
79668936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
79768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
79868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
79968936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
80068936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
801e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
80268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
80368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
80468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
805eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
806eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
807373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
8084dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
8090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
8100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
81182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
81282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
81382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
81470c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
81570c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
81682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
81782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
81882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
81982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
8200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
8210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
8220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
8230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
8240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
82582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sStatusUpdatesProjectionMap = columns;
82619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
8271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
8281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
8291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
8301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
8311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
8321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
8331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
8341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
8351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
8361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
8371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
8384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
8394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
8403296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
8413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        map.put(toField, fromField + " AS " + toField);
8423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    }
8433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
8443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
8453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
8463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
8473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
8483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
850653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
8513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8521129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
8533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
8543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
855a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
856a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
857a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
858a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
859a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
860a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
861a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
862a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
8633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
865653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
866653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
867b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
868653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
869653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
870653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
871653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
8723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
8743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
8755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
876e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
877e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
878e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
879e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
880653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
881e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
882e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
883e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
8843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
8883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
8893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
890653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
891f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
89214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
89314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
894653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
895653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
896653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
897653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
898653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
899653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
900653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
901653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
902653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
903653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
904653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
905653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
906653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
907653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
908653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
909653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
910653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
911653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " = " + dataId, null);
912653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
913653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
914f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
915653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
916653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
9173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
92014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
92114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
92214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
9233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
9243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
9253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
9265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
9273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
9293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
9325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, rawContactId);
9333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
93414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                setIsPrimary(rawContactId, newPrimaryId, getMimeTypeId());
9353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long rawContactId) {
939e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
940e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
9415ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Cursor c = queryData(db, rawContactId);
9423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
943e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
94414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
945f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
946e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
947e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
948e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
949e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
9503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
9513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
9523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
9533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
954e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
955e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
956e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
957e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
958e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
959e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
960e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
961e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
962e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
9633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long rawContactId) {
96614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return db.query(DataDeleteQuery.TABLE, DataDeleteQuery.CONCRETE_COLUMNS,
96714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId +
96814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
9693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
9703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
97225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
973285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
974d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
975285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateDisplayName(db, rawContactId);
976285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
9773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
978a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
979a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
980a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return true;
981a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
982622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
983622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
984622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
985622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
986622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
987622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
988622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
989622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
990622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=" + dataId,
991622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    null, null, null, null);
992622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
993622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
994622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
995622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
996622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        values.put(key, cursor.getString(i));
997622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
998622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
999622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1000622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1001622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1002622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1003622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1004622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
10083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
10103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1015622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
10163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1017622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
10183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1019622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
10203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10235ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1024622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
102514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
102614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
102714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1028f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1029f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
103025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
103114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
103214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
103314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
103414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
103514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1036f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1037622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1038622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1039cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1040622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1041622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
104214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1043f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
104414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1045f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
1046f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
1047f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
1048f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
104914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
105025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
105114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
105214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
105314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
105414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
105514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
105614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
105714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
105814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
105914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1060f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
106125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
106214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
10633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1066622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
10673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1068622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1069622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1070622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1071622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
10723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1073622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1074622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1075622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1076622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1077622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1078622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1079622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
108067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1081622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
108267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
108367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1084622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1085622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
10868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1087622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1088622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
108967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
109067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
109167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
109267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
109367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
109467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
10958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1096622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
1097622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(name);
1098622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
1099622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1100622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1101622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1102622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1103622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1104622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1105622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1106622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1107622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1108622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1109622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1110622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1111622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1112622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1113622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1114622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1116622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1117622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1118622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1119f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1120622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1121622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1122622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1123f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1124622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1125622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1126622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1127622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1128622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1130622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1131622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1132622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1133622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1134622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1135622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1136622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1137622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1138622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1139622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1140622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1141622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
114267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
114367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
114467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
114567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1146622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1147622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1148622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1149622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1150622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1151622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
115267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
115367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
115467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1155622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1156622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1157622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
11583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
11593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
11633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
11653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
11663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
11683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
11693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
11703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
11713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1175622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1176622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1177622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1179622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1180622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1181f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1182622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1183622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1184622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1185f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1186622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1188622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1189622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1190622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1192622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1193622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1194622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1195622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
11963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1197622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1198622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1199622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1200622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1201622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
12023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
12063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
12083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
12093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12125ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1213a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1214a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1215a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1216a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1217a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
121825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1219a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1220a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
12213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
122414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1225f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1226a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1227a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1228a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
122914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
123014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1231f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
123214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
123325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1234a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
1235a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
123614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
123714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
123814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
123914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1240a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
124114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
124214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
124425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1245a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
124614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
124714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
124814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
12503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
12513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
12523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
12533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
12543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
12553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
12563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1258a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1259a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1260a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1261a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1262a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
12633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1265e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1266e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1267e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1268e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1269e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1270e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1271e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
12725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
127314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
127414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
127514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
127614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
127725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1278f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
127914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
128014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
128114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
128214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
128314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1284f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
128514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
128614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
128714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
128814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1289f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
129014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1291f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1292f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
129325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
129414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
129514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
129714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
129814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
129914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
130014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
130114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
130214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1303f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
130425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
130514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1306e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1307e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1308e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1309e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1310e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1311e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1312e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1313e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1314e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1315e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1316e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1317e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1318e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1319e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
132014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
132114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
132314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
132414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
132514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
132614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
132714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
132814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
132914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
133114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1333f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
133414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
133514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
133614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
133814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1339f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
134014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
134114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
134214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
134314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1344f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
134514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1346f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1347f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
134825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
134914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
135014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
135214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
135314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
135414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
135514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
135714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1358f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
135925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
136014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
136114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
136214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
136314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
13643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
13653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
13673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
13683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
13720b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
13730b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
13740b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
13750b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1376653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13770b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1378653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13790b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1380285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
138125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
13820b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
13830b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
13840b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1385653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1386653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1387653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1388653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1389653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1390f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
139114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
139214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
13930b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
13940b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
13950b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1396653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1397f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
1398653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
13990b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1400285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
140125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
14020b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1403f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
14040b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
140514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
140614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
140714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
140814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
140914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
141014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
141114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
141314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1415285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
141625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
141714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1418653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1419653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1420653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1421e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1422e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1423e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1424e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1425653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1426653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1427653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1428e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1429653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1430653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1431e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1432653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
14335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1434653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1435e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
143636045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.MIN_MATCH,
143736045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                        PhoneNumberUtils.toCallerIDMinMatch(number));
143836045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov
1439653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1440653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
1441653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=" + dataId, null);
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();
162235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1623de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1624b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1625a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1626b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1627d076a108d58b30591f197e1b90fa8de60999c499Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper);
16280e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1629a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1630b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1631653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1632c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1633653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1634653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1635653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1636653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1637653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1638c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1639653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1640653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1641653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1642653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1643653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1644653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1645653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1646653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1647653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1648653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1649653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1650ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate = db.compileStatement(
1651ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "UPDATE " + Tables.CONTACTS +
1652ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
1653ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "WHERE " + Contacts._ID + "=?");
1654a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
165525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate = db.compileStatement(
165625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
165725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
165825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
165925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
16603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1661a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mLastStatusUpdate = db.compileStatement(
1662a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                "UPDATE " + Tables.CONTACTS +
1663a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1664a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1665a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1666a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1667a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1668a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1669a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1670a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1671a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1672a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
16730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
16740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1675a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                        " LIMIT 1)" +
1676a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1677e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1678622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        final Locale locale = Locale.getDefault();
167928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
168028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
168128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
168228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
1683622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1684622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                locale);
1685f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1686622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
16874097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1688f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1689f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1690f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1691f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1692f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1693f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1694f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1695a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateInsert = db.compileStatement(
1696a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1697a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
16980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
16990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1703a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1704a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateReplace = db.compileStatement(
1705a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1706a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
17080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1713a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1714a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateAutoTimestamp = db.compileStatement(
1715a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
17170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1718a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
17190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
17200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
17210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mStatusAttributionUpdate = db.compileStatement(
17220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
17240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
17250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
17260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1727a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1728a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateDelete = db.compileStatement(
1729a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1730a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1731a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
17323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
17333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1734e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
17353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
17363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
173767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
173867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
17393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
17403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
174114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
17423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
17433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1744622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1745622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1746a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1747a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
17483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
17493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1750568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            importLegacyContactsAsync();
17513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1752568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1753c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        verifyAccounts();
175470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
1755f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1756f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
17571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
17581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
17591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
17601129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
1761315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        preloadNicknameBloomFilter();
17621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
17634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
17644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1765c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
1766c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1767c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1768c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
1769c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
177031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1771de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1772b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1773b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
177431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
177531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1776013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1777013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1778013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1779013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
17803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
17813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
17823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
17833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
17843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1785568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1786568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1787568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1788568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1789568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1790568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
1791568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
1792568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1793568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
1794ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        mAccessLatch = new CountDownLatch(1);
1795568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1796568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
1797568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
1798568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
1799568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                if (importLegacyContacts()) {
1800d076a108d58b30591f197e1b90fa8de60999c499Dmitri Plotnikov                    // TODO aggregate all newly added raw contacts
1801568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1802568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    /*
1803568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * When the import process is done, we can unlock the provider and
1804568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * start aggregating the imported contacts asynchronously.
1805568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     */
1806ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch.countDown();
1807ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1808568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
1809568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1810568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
1811568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1812568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
1813568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1814568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18153d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
1816568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1817568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        if (importLegacyContacts(importer)) {
18183d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
18203d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
18213d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
18223d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18233d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
18243d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
18253d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18263d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18273d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
18283d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1829568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
18300e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
18313d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
18323d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
18333d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
18340e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff            mContactAggregator.setEnabled(aggregatorEnabled);
18353d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
18373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
18383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
18393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1842a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1843a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1844a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1845a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1846b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1847a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1848a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1849568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1850568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1851568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1852568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1853568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1854568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1855568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1856568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
1857ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
1858ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
1859ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
1860ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
1861ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
1862ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1863ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
1864ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
186581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
1866ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
1867ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1868568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1869568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1870568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1871568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1872568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1873568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1874568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1875568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1876568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1877568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1878568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1879568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1880568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1881568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1882568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1883568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1884568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
1885568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1886568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1887568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1888568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1889568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1890568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1891568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
1892568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1893568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1894568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1895568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1897285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1898bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1899b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1900b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1901285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
19021ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1903b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1904b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1905b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1906b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
1907285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
1908b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
1909df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
1910a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
1911285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1912285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1913285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1914285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
19151129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
1916bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1917b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1918b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1919285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1920b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
19211ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
19221a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
19231a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1924b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
19251a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
1926b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1927b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1928b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1929bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1930b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1931b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
19321129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
193308e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
1934d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
1935d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
1936285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1937b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1938a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
1939a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1940a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
1941a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
1942a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1943a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1944a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
1945a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1946b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
1947a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
1948a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
1949a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
1950a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
1951a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
1952b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1953b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1954b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
1955b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
1956b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().update(mDb, id, entry.getValue());
1957b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1958b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1959b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1960b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1961b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1962a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
1963a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
1964a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
1965a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
1966a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
1967b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1968a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
1969b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1970a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
1971a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
1972285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1973285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1974285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1975cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
197681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
197781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
197881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
197981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
198081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
198181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
198281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1983cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1984568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1985285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
1986ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
1987285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1988285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
19893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
19903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
19913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
19923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
19933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
19943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
19953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
19963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
19973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
19984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1999de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2000bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
20011129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2002b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2003f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2004f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2005f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2006f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2007a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2008a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
200935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2010a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
201135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2012b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
201335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
201435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2015d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2016d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
20176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
20196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
20205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2021f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertRawContact(uri, values);
2022f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2023a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2024a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2025a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
20275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2028f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2029f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2030a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2031a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2032a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2033a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2034f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2035f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2036a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2037a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2038a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2039ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2040f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2041f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2042ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2043ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2044ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2045eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
20465aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
204743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2048eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2049eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2050eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
205182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
205282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
20531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
20541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
20551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2056a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
205781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2058f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2059a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2060a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
20627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
20637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
20647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2065de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2066a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2067a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2068a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2069035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
2070035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
2071f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the ContentValues to read from and update
2072f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the explicitly provided Account
2073f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @return false if the parameters are inconsistent
20747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2075f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private boolean resolveAccount(Uri uri, ContentValues values) {
2076f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2077f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2078f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2079f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2080f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = null;
2081f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = null;
2082f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2083f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2084f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2085f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2086f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2087f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(valueAccountName) && TextUtils.isEmpty(valueAccountType)) {
2088f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2089f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2090f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
2091f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (accountName != null && !accountName.equals(valueAccountName)) {
2092f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return false;
2093f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
2094f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2095f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (accountType != null && !accountType.equals(valueAccountType)) {
2096035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
2097035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
2098f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2099f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2100f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2101f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2102f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2103f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
2104f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = null;
2105f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return true;
2106035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2107f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2108f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2109f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2110f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2111f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2112035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2113f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2114035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
21157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
21167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2118d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
21196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
21206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
21216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
21226bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2123d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2124de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
21256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
21266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2128a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2129a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2130f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2131f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2132a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2133a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2134f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertRawContact(Uri uri, ContentValues values) {
2135f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2136f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2137f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2138f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2139f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
21407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
21417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
21443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2145f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
21463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
21473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2148f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2149023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
2150285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2151285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2152f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mInsertedRawContacts.put(rawContactId, mAccount);
2153f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2154023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2155a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2156a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2157a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2158a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2159a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2160a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2161a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2162a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2163f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2164a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2165de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2166de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
216767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2168de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
216920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2170de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2171de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2172de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2173b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2174de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2175de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2176508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2177de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2178de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2179de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2180de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2181de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
21824097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2183b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2184de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2185a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2186a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2187a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2188f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2189de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2190a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2191b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2192a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2193a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2194a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2195a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2196a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
21974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
21984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
21998e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
22008e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
22018e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
22028e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
22038e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2204b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2205f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
22068e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
22078e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
22088e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22098e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2210421782cb554e5050cf62a86b98df6520038dcd15Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId);
2211f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22128e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
22138e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22148e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2215b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2216f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
22178e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
22188e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
22198e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2220f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22218e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2222f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2223c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2224b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
22258e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2226f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22278e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2228f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2229f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2230f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2231a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
22325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
22339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
22349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
22355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
22369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
22379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
22389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
22399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
22409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2241ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2242ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2243ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2244ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
2245ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
2246ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    RawContacts._ID + "=" + rawContactId, null, null, null, null);
2247ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2248ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2249ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2250ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2251ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2252ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2253ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
22549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2255ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2256ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
22579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
22589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2259ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
22609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
22619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2262ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
22639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
22649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
22659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2266ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2267ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2268ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2269ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2270ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2271ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2272ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2273ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2274ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2275ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2276ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2277ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2278ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2279ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2280ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2281ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2282ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2283ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2284ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2285ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
22869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
22875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2288ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
22899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2290df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
22919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2292ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2293ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
22949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
22959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2296df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2297df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
22989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
22999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
23009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
23019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
23029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
23039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2304ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
23059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
23069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
23079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
23089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2309ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2310ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
23119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
23129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2313d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
23141129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
23151129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
23161129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
23171129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
23181129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
23191129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Organization.TITLE +
23201129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
23211129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
23221129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
23231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2324d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2325d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2326d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
2327d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int DATA = 2;
2328d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int TITLE = 3;
2329d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2330d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2331d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2332d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2333d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2334d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2335d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2336d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        String bestDisplayName = null;
2337d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
2338d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
23391129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
23401129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2341d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2342d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
23431129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
23441129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
23451129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                // Display name is at DATA1 in all type. This is ensured in the
23461129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                // constructor.
23471129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                mCharArrayBuffer.sizeCopied = 0;
23481129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                c.copyStringToBuffer(DisplayNameQuery.DATA, mCharArrayBuffer);
23491129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                if (mimeType == mMimeTypeIdOrganization && mCharArrayBuffer.sizeCopied == 0) {
23501129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
2351d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
23521129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
23531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                if (mCharArrayBuffer.sizeCopied != 0) {
23541129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    int source = getDisplayNameSource(mimeType);
23551129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    if (source > bestDisplayNameSource) {
2356d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
23571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
23581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
23591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                    } else if (source == bestDisplayNameSource
23601129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            && source != DisplayNameSources.UNDEFINED) {
23611129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        if (mimeType == mMimeTypeIdStructuredName
23621129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                || c.getInt(DisplayNameQuery.IS_PRIMARY) != 0) {
23631129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
23641129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
23651129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
23661129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2367d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
2368d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2369d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2370d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2371d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2372d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2373d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2374d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2375d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
2376d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2377d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
23781129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
23791129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
23801129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
23811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
23821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
23831129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
23841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
23851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
23861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
23871129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
23881129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
23891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
23901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
23911129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
23921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
23931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
23949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
239520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
239620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2397f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
239820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
239920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2400de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2401de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
240214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2403de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2404de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
240514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
240614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2407a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2408a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2409f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
241088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
2411a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    if (rowHandler.isAggregationRequired()) {
2412a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        triggerAggregation(rawContactId);
2413a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    }
241488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
241520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
241620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2417de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
241820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
241920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
242020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
242120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
242220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
242388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
242488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
242588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
242620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2427f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
242888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
242988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
243014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=" + dataId, null,
243114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                null);
2432f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
243320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
243420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
243520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
243620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
243720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
243814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
243920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
244020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
244120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
244220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
244320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
244420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
244520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
244620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
244720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
24487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
244920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
245020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
245120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2452a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2453a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = rowHandler.delete(mDb, c);
24548e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2455a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            if (rowHandler.isAggregationRequired()) {
2456a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                triggerAggregation(rawContactId);
2457a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            }
24588e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return count;
245920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
246020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
246120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
246220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
246320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
246420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2465ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2466ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2467f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2468f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2469f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2470f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2471f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (!resolveAccount(uri, mValues)) {
2472ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
2473ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2474ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2476f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
247767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2478f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
247967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2480f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2482f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2483f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
248473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
248573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2486f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2487ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2488f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
24891a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2490ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2491ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2492ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2493ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2494ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
24955aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2496e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
24975aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
24981a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
24991a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2500e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
25011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2502e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2503e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2504e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2505ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
250682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
25071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
250882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
250982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
25100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
25114dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
25124dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
25130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
251482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
25154dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
25164dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
25174dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
25184dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
25191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
25201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2521dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2522dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
252382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2524f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
2525dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2526dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2527dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2528f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=");
2529f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(dataId);
25301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2531dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2532dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
25330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
25340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
25350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
25360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2537dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2538dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2539dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2540dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
2541f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2542f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2543f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2544f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2545f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2546f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2547f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
2548f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (")
2549f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2550f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(",")
2551f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2552f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(")" + " AND " + Data.DATA1 + "=");
2553f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2554f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND ((" + DataColumns.MIMETYPE_ID + "=")
2555f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2556f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2557f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol);
2558dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2559f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2560f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2561dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2562f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=")
2563f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2564f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append("))");
2565dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
2566f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=")
2567f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2568f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2569f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol)
2570f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.DATA + "=");
2571f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2572dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2573f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2574f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2575dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2576dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
25771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
257882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
2579f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=")
258082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                        .append(values.getAsLong(StatusUpdates.DATA_ID));
2581dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
258270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2583f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
258470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
25851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
25861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2587de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
2588c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    mSb.toString(), null, null, null,
2589c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
25901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
259167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
25925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2593e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
25941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
25951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
25961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
25971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
25981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
259931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
260031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
260131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
26021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
26031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
260482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2605a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2606a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2607a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2608a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2609a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2610a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2611a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
261282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2613a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2614a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
261582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
261682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
261782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
261882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
261982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2620a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
262182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
262282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
26231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2624a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2625a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2626a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2627e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
26280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
262982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
263082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
26310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
26320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
26330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
26350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
26360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
26370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
26380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
26390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
26410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
26420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2643a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
2644a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
2645a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
264682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
264782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
2648a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
2649a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
26500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 3, status);
26510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 4, resPackage);
26520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 5, iconResource);
26530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 6, labelResource);
2654a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
2655a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
2656a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2657a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
2658a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
26590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 2, status);
26600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 3, resPackage);
26610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 4, iconResource);
26620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 5, labelResource);
2663a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
2664a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
2665a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
26660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
2667a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
26680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 2, status);
2669a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
26700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 4, status);
2671a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
26720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 1, resPackage);
26740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 2, iconResource);
26750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 3, labelResource);
26760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
26770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
2678a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
2679e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2680e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2681bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2682a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2683a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
2684a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
2685a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
2686a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2687a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2688a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
26891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
26901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
26914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2692de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2693bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2694b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2695b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2696b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2697f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2698f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2699508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2700508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
270135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2702b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
270335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2704b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2705b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2706b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2707b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2708b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2709b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2710cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2711cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2712cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2713cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2714cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2715d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2716d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2717cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
27186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
27196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
27202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
27212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
27222e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
27232e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
27242e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
27252e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
27262e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
27272e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
27282e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
27292e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
27302e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
27312e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
27322971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
27332971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
27342971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2735e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27362971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
27372971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
27382971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2739f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        numDeletes += deleteRawContact(rawContactId, callerIsSyncAdapter);
27402971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
27412971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27422971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27432971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
27442971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
27452971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
27462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
27475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
27482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2749f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteRawContact(rawContactId, callerIsSyncAdapter);
2750508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2751508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
275220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2753f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2754944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2755f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
275620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
275720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
275848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
275948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
276048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
276148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2762508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2763f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2764f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteData(Data._ID + "=" + dataId, null, callerIsSyncAdapter);
2765ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2767ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2768f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
27695aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
27702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
27712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
27722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
27732971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
27742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2775e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
27772971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
27785aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
27792971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
27802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27812971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27822971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
278381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2784f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
278581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
27862971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2787508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2788508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2789eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
279043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
27915aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteSettings(uri, selection, selectionArgs);
2792eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2793eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
279482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
27950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
27961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
27971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
279881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
279981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
28003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
280181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2802508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
28034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
28044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
28055aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2806ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
2807b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
280894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2809de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
281094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
281194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
281294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
281394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2814f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2815de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
281694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
281794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
281894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2819f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2820de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
282194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
282294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
28231a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
282494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
282594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
282694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
28275aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2828e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
28291a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2830e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2831e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2832e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2833cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
2834cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2835cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
2836cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2837cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2838cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2839cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
2840cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2841cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2842cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2843cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2844cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2845cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2846cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2847cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2848f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    public int deleteRawContact(long rawContactId, boolean callerIsSyncAdapter) {
28493389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
2850f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
285114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2852de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
285333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2854b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2855cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
285633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
285733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
285833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
28590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
28609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
28619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
28629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
28639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
28649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
28659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
28669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
28679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
28680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
28690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2870cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
287181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
287281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2873cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2874cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2875cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2876cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2877cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2878cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2879cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
2880cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2881cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
28824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2883de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2884de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2885bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2886b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2887b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2888b5a4add17815167d20a90645779df34cdf45280dFred Quintana
288935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
289000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
289100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2892b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2893b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
28941129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
2895b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
2896b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2897b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2898b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2899f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2900f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
290100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
290235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2903b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2904b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2905b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2906b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2907b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2908b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2909b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2910b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2911b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2912b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2913b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
291435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2915d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
29168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
291700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
291800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
291900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2920d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
29218c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
2922c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2923c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2924c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
29252e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
29262e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
29272e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
29282e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
29292e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
29302e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
29312e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
29322e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
29332e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
29348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
29352e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
29362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
29372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
29387d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
29397d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
29407d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
29417d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
29427d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
29437d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
29447d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
29457d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
29467d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
29477d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
294820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2949944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2950f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
295181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2952f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
295381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
295420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
295520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2956c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
295748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
295848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
295948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
296048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2961f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
296281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2963f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
296481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
296500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
296600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
29677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
29685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
29695ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
29704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
29717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
29727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
29737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
29745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
297533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
29764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
29774529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId
29784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
29794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
298027f039b535f98c1cb1a31207047003235ddaed15Dmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId, null);
29814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
29827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
29837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
29847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2985ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
29865aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2987f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
298881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2989f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
299081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2991ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2992ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2993ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2994ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2995ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
299673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                String selectionWithId = (Groups._ID + "=" + groupId + " ")
299773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
29985aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
29995aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
300081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3001f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
300281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3003ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3004ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3005ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3006127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3007de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3008b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3009b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3010b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3011eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
30125aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateSettings(uri, values, selection, selectionArgs);
301343880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3014eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3015eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3016eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
30179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
30189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
30199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
30209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
30219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
302281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
302381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3024f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
302581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
302600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
302700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
302800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
30294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
30304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
30319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
30329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
30339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
30349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
30359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
30369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
30379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
30389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
30399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
30409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
30419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
30429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
30439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
30459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
30469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
30479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
30489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
30499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
30509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
30519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
30529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
30539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
30569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
30579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
30589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
30599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
30609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
30619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
30629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
30639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
30649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
30679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
30689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
30699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
30709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
30719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
30729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
30739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
30749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
30759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
30769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
30779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
30789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
30799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
30829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
30839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
30849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
30859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
30869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
30879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
30885aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3089f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
309073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3091ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3092ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
309373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3094f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
309573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
309673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
309773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
309873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
309973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
310073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
310173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
310273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3103ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
31041a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
31051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
310694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
31076ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
31081129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
31096ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            final long groupId = ContentUris.parseId(uri);
31106ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
31116ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    Groups.ACCOUNT_TYPE}, Groups._ID + "=" + groupId, null, null,
31126ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
31136ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
31146ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
31156ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
31166ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
31176ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
31186ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
31196ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
31206ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3121ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
31226ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
31236ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
31246ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
31256ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
31266ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
31276ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
31286ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
31296ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
313094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
313194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
313294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3133b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3134b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3135e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
31361a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31371a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3138e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3139e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3140e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3141e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
31424529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
31434529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
31444529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
31454529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
31464529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
314773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
31484529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3149b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
315051bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
31514529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
31524529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
31534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
31544529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
31554529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
31564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
31574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
31584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
31594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
31604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
31614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
31624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
31634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
31644529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
31654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
316619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final String selection = RawContacts._ID + " = " + rawContactId;
316719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
316819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
316919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3170ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3171ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
317219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
317319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
317419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    null, null, null, null);
317519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
317619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
317719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3178ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3179ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
318019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
318119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
318219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
318319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
318419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
318519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
318619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
318719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, null);
31885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3189433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
31904529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3191433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3192285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
3193285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateLookupKey(mDb, rawContactId);
3194285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
319519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
319619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
3197ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
319819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
31995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
32005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
320133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
320233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3203321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3204f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
320520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
320620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
320720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
32085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
320920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
321020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
321120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
321220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
321320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3214b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
321520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
321620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
321770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
321870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
321920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
322020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
322120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
322270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
322320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
322470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
322520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
322670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
322720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
322870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
322920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
323020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3231653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
323220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3233653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3234653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
323514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3236653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3237653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3238f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
323920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3240653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3241653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
324220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
324320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3244653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
324520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
324620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3247f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3248653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3249653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3250321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3251653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
325214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3253a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3254f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
32558e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
3256a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
3257a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
3258a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
32598e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
3260653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return 1;
3261321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3262321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
32638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
32648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
32658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3266b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
32678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
32688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
32698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
32708c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
32718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
32728c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
32738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
32748c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
32758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
32768c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
32778c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
32788c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
32798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
32808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
32818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
32828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3283d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
32848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3285b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3286d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3287b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3288d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3289b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3290d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3291b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3292d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3293b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3294d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3295d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3296d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
32978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3298d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3299d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3300d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
33018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3302c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
33038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3304c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3305c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
33068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=" + contactId, null);
33078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
33098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
33108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3311b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
33128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3313b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
33148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3315b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
33168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3317b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
33188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3319b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
33208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
33218c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
33228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=" + contactId, null);
3323f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3324d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3325ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    public void updateContactLastContactedTime(long contactId, long lastTimeContacted) {
3326ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
3327ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(2, contactId);
3328ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.execute();
3329d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
3330d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3331127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3332127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
33330c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
33340c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
333580c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
33360c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
33370c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
33380c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
33390c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
33400c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
33410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
33420c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3343b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3344127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
33450c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
33460c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
33470c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId1 + " AND "
33480c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId2, null);
33490c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
33506bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
33516bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
33520c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
33530c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
33540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
33550c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3356127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3357127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
33583389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3359dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
3360dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
3361dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3362b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
33630c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
33640c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
3365b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
33660c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
3367127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3368127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3369127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3370127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3371b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3372b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
337370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3374b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
337570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        if (mDb == null) return;
337670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
3377627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
3378627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        boolean hasUnassignedContacts[] = new boolean[]{false};
337970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
338070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3381627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3382627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.RAW_CONTACTS, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE);
3383627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3384627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.GROUPS, Groups.ACCOUNT_NAME, Groups.ACCOUNT_TYPE);
3385627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3386627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE);
338748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3388627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3389627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // in the existingAccounts set will be extra accounts whose data must be deleted.
3390627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3391627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3392627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
339370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
339470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
339570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
33965f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
3397627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                String[] params = new String[] {account.name, account.type};
3398627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3399627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.GROUPS +
3400627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3401627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3402627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3403627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.PRESENCE +
3404627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3405627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                "SELECT " + RawContacts._ID +
3406627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
3407627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3408627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3409627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3410627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.RAW_CONTACTS +
3411627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3412627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3413627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3414627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.SETTINGS +
3415627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3416627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3417627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3418627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3419627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            if (hasUnassignedContacts[0]) {
3420627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3421627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                Account primaryAccount = null;
3422627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                for (Account account : accounts) {
3423627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    if (isWritableAccount(account)) {
3424627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        primaryAccount = account;
3425627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        break;
3426627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    }
3427627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3428627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3429627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (primaryAccount != null) {
3430627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    String[] params = new String[] {primaryAccount.name, primaryAccount.type};
3431627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3432627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3433627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.RAW_CONTACTS +
3434627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + RawContacts.ACCOUNT_NAME + "=?,"
3435627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + RawContacts.ACCOUNT_TYPE + "=?" +
3436627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
3437627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL", params);
3438627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3439627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // We don't currently support groups for unsynced accounts, so this is for
3440627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // the future
3441627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3442627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.GROUPS +
3443627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + Groups.ACCOUNT_NAME + "=?,"
3444627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + Groups.ACCOUNT_TYPE + "=?" +
3445627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " IS NULL" +
3446627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + Groups.ACCOUNT_TYPE + " IS NULL", params);
3447627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
344870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
3449627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3450b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
345170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
345270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
345370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
345470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
345570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3456619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3457619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3458627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3459627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3460627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    private void findValidAccounts(Set<Account> validAccounts, boolean[] hasUnassignedContacts,
3461627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            String table, String accountNameColumn, String accountTypeColumn) {
3462627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        Cursor c = mDb.rawQuery("SELECT DISTINCT " + accountNameColumn + "," + accountTypeColumn
3463627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                + " FROM " + table, null);
3464627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3465627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3466627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (c.isNull(0) && c.isNull(1)) {
3467627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    hasUnassignedContacts[0] = true;
3468627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                } else {
3469627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3470627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3471627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3472627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3473627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3474627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3475627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3476627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3477627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
3478622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
3479622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
348067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
348167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
348267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
348367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
348467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
348567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
348667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
348767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
348867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
348967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
349067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
349167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
3492dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
3493622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
3494dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
3495dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
3496622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
3497622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
3498dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
3499622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
3500622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
35014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
35024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
35034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
3504bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3505bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3506bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
35070b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3508b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
350935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3510d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
35111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3512c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3513c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3514619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3515619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3516a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
35174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
351835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3519b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
352035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
352135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3522d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3523763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3524619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3525619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3526619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3527d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
35284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3529763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
35304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + contactId);
35316bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
35326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
35336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
35345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
35355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
35365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
35375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
35385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
35395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
35405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
35425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
35435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
35445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3545763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
35465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=" + contactId + " AND " +
35475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            Contacts.LOOKUP_KEY + "=");
35485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhereEscapeString(lookupKey);
35495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, selectionArgs, sortOrder,
35505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
35515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
35525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
35535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
35545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
35565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3558763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
35595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
35605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
35615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3563f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3564f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
3565f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
3566763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3567f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
3568f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
3569f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3570f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3571f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
3572ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3573763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3574ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
35754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
35764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3577e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
35785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
35794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sb.toString());
3580ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3581ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3582ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3583ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3584ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3585ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
35864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3587ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3588d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
35894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
35904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3591e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
35925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
35934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
35944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
35954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3596763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3597ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
35985e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
35995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
36005e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
36015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
36035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
36045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
36054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
36064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
36074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3608d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
36095e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
36105e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
36114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3612d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3613d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3614d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3615763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
36164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
36174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3618d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
36195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
36205e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3621d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3622d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
36234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3624d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3625d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3626d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3627d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
36284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
36294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3630d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3631d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3632d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3633d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3634d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3635d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3636ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3637763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3638b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
363971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
36404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3641b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3642b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3643b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3644b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3645d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
36464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
364782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
36496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
36506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
365100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3652ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
36533653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
365482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
36553653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
36563653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
36573653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
36583653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
36593653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
36604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
366182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
366289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
36632815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
36642815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
36652815f58f72f109790585931f601a63ddc02536a5Evan Millar
366648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
366782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
366848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
366948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
367048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
367148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
367248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3673ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
367482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
367589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3676ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
36774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3679a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
36805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
36815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
36825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
36835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
36845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
368520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
36865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
36875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
36885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
36895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
36905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
36915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
36925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
36935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
36945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
36955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
36965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
36975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
36985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
36995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
37005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
37015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
37025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3703a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
3704ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
37055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3706a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3707a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3708a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3709ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3710ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3711ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
37124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
371382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
371489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
37154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
37164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
37174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
371848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
371982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
372048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
372148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
372248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
372348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
372448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
37255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
372682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
372789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
37284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
37295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + Email.DATA + "=");
37304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhereEscapeString(uri.getLastPathSegment());
37314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3732ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3733ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3734ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
37355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
373682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
373789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
37385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
37395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
37405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3741a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
37425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
374320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
374420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
374520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
374620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            sb.append(Data.RAW_CONTACT_ID + " IN ");
374720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
374820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            sb.append(" OR ");
374920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
37505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
37515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
37525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(Email.DATA + " LIKE ");
37531e530df9f7e496dc47f77d4323c89bd413b79b64Dmitri Plotnikov                    sb.append(DatabaseUtils.sqlEscapeString(filterParam + '%'));
37545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
3755a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
37565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
37575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3758a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3759a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3760a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
37615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
37625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
37635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3764ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
376582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
376689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
376789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3768ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3769ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3770ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
377148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
377282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
377348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
377448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
377548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
377648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
377748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
377848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
37795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
3780763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
37814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
37824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
37834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
37855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
3786763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
378789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
37884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
37894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
37904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
37925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
379382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
379489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=" + rawContactId);
3795e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3796e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3797e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3798e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
379982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3800e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3801e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3802e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
38034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
380482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
380582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + ContentUris.parseId(uri));
3806a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3807a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3808a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3809a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
38104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3811a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3812a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3813a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3814e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
3815a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3816a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3817e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3818b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
3819e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3820e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
3821e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3822e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3823e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3824a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3825a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3826a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3827ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
3828b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3829ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
383089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3831ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3832ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3833ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3834ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3835ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
3836b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3837ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
383889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(Groups._ID + "=" + groupId);
3839ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3840ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3841ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3842ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
3843b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3844ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
384589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
384689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3847ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3848ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3849ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3850b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
38510c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3852b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3853b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3854b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3855b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
385631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3857d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
38582d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
38592d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
38602d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
38612d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
386231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3863d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3864d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
386531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
386631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
386731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
386831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3869763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
38707581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
38717581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
38722d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
387331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
387431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3875eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3876eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3877eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
387889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3879e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3880e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3881e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3882b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3883e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
388482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3885b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3886e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3887e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
388882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3889b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3890e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3891e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3892e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3893eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3894eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3895eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
389682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
38970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
38985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
38995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
39005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
390182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
39020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
39030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri));
39045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
39055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
39065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3907c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3908a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3909c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3910c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3911c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
3912b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3913b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
3914c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3915c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
39161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
3917b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
39191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
39201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
39211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
3922b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
39241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
39251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
39261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
39271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
3928b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
39301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
39311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
39321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
39331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
3934b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
39351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
393671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
39371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
39381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
39391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
394046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
394146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
394246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
394346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
394446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
394546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
394646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
394746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
394846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
394946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
395046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
395146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
39524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
3953f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3954c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
39554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
39564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
39575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
39585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
39595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
39615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
39625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
3963038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
3964038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
3965038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
3966038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
39675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
39685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
39694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
39704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
39714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
39724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
39734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
39744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
39755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
39765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
39775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
39785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
39805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (contactId == -1) {
39815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
39825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
39855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
39865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
39885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
39895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
39915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
39925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
39935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
39945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
39955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
39965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
39985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
39995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
40005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
40015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
40045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
40055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int sourceIdCount = 0;
40065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
40075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
40085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
40095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sourceIdCount++;
40105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (sourceIdCount == 0) {
40145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
40155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
40185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
40195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
40205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
40215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
40225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
40235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
40245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
40255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
40285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
40295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
40315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
40325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
40335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
40345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
40355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
40365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
40375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
40385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
40395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
40405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
40415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
40425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
40435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
40445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
40455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
40465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
40475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
40495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
40505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
40535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupByDisplayNameQuery {
40565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
40575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
40595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
40605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
40615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
40625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
40635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
40645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
40665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
40675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
40685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int NORMALIZED_NAME = 3;
40695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
40725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
40735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int displayNameCount = 0;
40745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
40755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
40765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
40775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                displayNameCount++;
40785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
40825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
40835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
40865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
40875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
40885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
40895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
40905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
40915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
40925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
40935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
40965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
40975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
40985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
40995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
41005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
41015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
41025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
41035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
41045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
41055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
41065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
41075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
41085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
41095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
41105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
41115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
41125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
41135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
41145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
41185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
41195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
41225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
41255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
41265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
41275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
41285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
41295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
41315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
41325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
41345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
41355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
41375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
41385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
41395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
41405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
41415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
41425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
41435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
41445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
41455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
41465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
41485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
41495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
41535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
41545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
41555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
41565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
41575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
41585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4159763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4160763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
416182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4162763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4163f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4164763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4165763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4166d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4167763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4168763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
4169b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
417082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
417182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
417282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
417382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
4174b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
417582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
417682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
417782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
417882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
417982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
41803296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
41813296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
4182a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
41833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
418482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
418582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
418682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
418782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
4188ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
4189763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4190763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
4191763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4192f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4193763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4194763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4195d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4196763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4197763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
4198763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4199763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4200763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4201763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4202763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
420346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
420446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        // Note: currently, "export only" equals to "restricted", but may not in the future.
420546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
420646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.FOR_EXPORT_ONLY, false);
420746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
4208f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
420946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
421046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        if (requestingPackage != null) {
421146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            excludeRestrictedData = excludeRestrictedData
421246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
421346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        }
421446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
421546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setProjectionMap(sRawContactsEntityProjectionMap);
421646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
421746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
421846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
421982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
422082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
422182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4222d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa        // Note: currently, "export only" equals to "restricted", but may not in the future.
4223763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
4224d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa                Data.FOR_EXPORT_ONLY, false);
4225763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4226f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
4227763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4228763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4229763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            excludeRestrictedData = excludeRestrictedData
4230d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4231763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4232763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4233763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getDataView(excludeRestrictedData));
423482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
423582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
42363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated presence when requested
4237b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
423882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
42393296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
424082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
424182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
424282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
42433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated status updates when requested
4244b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
424582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS,
424682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
424782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
424882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
424982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
42503296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
42513296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
425282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
42533296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
4254ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
42553296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
42563296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual presence when requested
42573296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
42583296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
42593296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdates.DATA_ID + "="
42603296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + DataColumns.CONCRETE_ID + ")");
42613296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
42623296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
42633296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual status updates when requested
42643296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection,
42653296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS,
42663296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_RES_PACKAGE,
42673296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_ICON,
42683296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_LABEL,
42693296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_TIMESTAMP)) {
42703296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
42713296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
42723296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + DataColumns.CONCRETE_ID + ")");
42733296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
42743296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
427582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
427682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
427782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4278ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4279ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
42800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
42810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
42820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4283b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
42840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
42850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4286b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
42870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
42880a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
42890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
42900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
42910a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4292b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
42930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
42940a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
42950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
42960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
42970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
42980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
42990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
43000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
43010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
43020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setTables(sb.toString());
43030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
43040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
43050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
43064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
4307f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4308f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
43094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
43104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
43114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
43124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
43134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
43144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
43154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
43164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
43174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
43184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4319e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
4320f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
4321f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
4322e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        if (!TextUtils.isEmpty(accountName)) {
4323e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
4324e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4325e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
4326e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
4327e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
4328e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
4329e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
4330e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
4331e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
4332e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
4333e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
4334e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
4335e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
4336e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
4337e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
43387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
4339c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4340c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
4341c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4342c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4343c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
4344f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
4345f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String limitParam = getQueryParameter(uri, "limit");
4346c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
4347c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4348c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4349c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
4350c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
4351c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
4352c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
4353c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
4354c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
4355c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4356c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
4357c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
4358c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
4359c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4360c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4361c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4362c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
43635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
43645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
43655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
43665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
43675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
43685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
43695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
43705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
43715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
43725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
43735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
43745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
43755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
43765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
43775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
43785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
43795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
43805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
43815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
43825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
43835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
43845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
43855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
43865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
43875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
43885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
43895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
43905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
43915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
43925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
439300ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
4394d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
439570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
439670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
43976cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
439870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
439970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
440070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
440170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
4402d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
440370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
440467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
44055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
44065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4407619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
4408619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
4409619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4410b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
4411b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
4412b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
4413b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
4414d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
4415b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                if (!"r".equals(mode)) {
4416b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
4417b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
4418b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4419b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4420b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4421b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                String sql =
4422b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
4423b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
4424b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=" + contactId;
4425b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
4426b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql, null);
4427d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4428d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4429f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4430d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
4431d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
443214b8a1243ab5c043b35e47527ca1c962064f3771Daisuke Miyakawa                final String selection = Contacts._ID + "=" + contactId;
4433d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4434d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
4435d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
4436d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
4437d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
4438d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                outputRawContactsAsVCard(localStream, selection, null);
4439d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
4440d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4441b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4442b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
4443b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new FileNotFoundException("No file at: " + uri);
4444b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
4445b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
4446b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4447d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
4448d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String VCARD_TYPE_DEFAULT = "default";
4449d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4450d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4451d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
4452d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
4453d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4454d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
4455d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
4456d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
4457d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
4458d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4459d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
4460d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
4461d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4462d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
4463d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
4464d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
4465b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4466d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
4467d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
4468d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
4469d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4470d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
4471d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4472d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4473d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4474d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
4475d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
4476d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
4477d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4478d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
4479d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
4480d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
4481d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
4482d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
4483d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4484f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
4485d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        if (!composer.init(selection, selectionArgs))
4486d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
4487d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4488d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
4489d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
4490d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
4491d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4492d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4493d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
4494d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4495b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4496619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
44977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
44987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
44997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
4500d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static class RawContactsEntityIterator implements EntityIterator {
45017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
45027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
45037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
45057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
45067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
45077a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
45087a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
45097a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
45107a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
45117a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
45127a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
45137a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
45147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
45157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
45167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
45177a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
45187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
45197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
45207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
45217a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
45227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
45237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4};
45247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
452546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public static final String[] PROJECTION = new String[]{
45266cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
45276cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
45286cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.SOURCE_ID,
45296cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.VERSION,
45306cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.DIRTY,
453146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts.Entity.DATA_ID,
45327a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RES_PACKAGE,
45337a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.MIMETYPE,
45347a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
45357a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
45367a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
45377a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
45387a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
45397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
45407a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
45417a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
45427a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
45437a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
45447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
45457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
45467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
45477a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
45487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
45497a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
45507a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
45517a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
45527a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4,
455346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts._ID,
45547a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.IS_PRIMARY,
45553cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar                Data.IS_SUPER_PRIMARY,
45567a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA_VERSION,
45577a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                GroupMembership.GROUP_SOURCE_ID,
45587a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC1,
45597a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC2,
45607a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC3,
456194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                RawContacts.SYNC4,
456238446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey                RawContacts.DELETED,
4563c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.CONTACT_ID,
4564c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.STARRED};
4565035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
4566035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
4567035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
4568035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
4569035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
4570035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
4571035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
457267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
457367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
457467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
45757a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_RAW_CONTACT_ID = 27;
45767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_IS_PRIMARY = 28;
45773cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_IS_SUPER_PRIMARY = 29;
45783cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DATA_VERSION = 30;
45793cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_GROUP_SOURCE_ID = 31;
45803cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC1 = 32;
45813cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC2 = 33;
45823cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC3 = 34;
45833cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC4 = 35;
45843cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DELETED = 36;
45853cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_CONTACT_ID = 37;
45863cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_STARRED = 38;
45877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
458846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public RawContactsEntityIterator(ContactsProvider2 provider, Uri entityUri,
458946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String contactsIdString,
459046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String selection, String[] selectionArgs, String sortOrder) {
45917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
459246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            Uri uri;
45937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
459446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, contactsIdString);
459546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(uri, RawContacts.Entity.CONTENT_DIRECTORY);
459646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            } else {
459746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = ContactsContract.RawContactsEntity.CONTENT_URI;
4598035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
459946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            final Uri.Builder builder = uri.buildUpon();
460046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            String query = entityUri.getQuery();
460146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            builder.encodedQuery(query);
460246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            mEntityCursor = provider.query(builder.build(),
460346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    PROJECTION, selection, selectionArgs, sortOrder);
46047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
46057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
46067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4607038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4608038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4609038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4610038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4611038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4612038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4613038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana
46147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
46157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
46167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
46177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
46187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
46197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
46207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
46217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
46237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
46247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
46257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
46267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
46287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
46297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
46317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
46327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
46337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
46347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
46357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
46367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
46377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
46397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46407a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
46417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
46437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
46446cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
46456cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
46465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
46476cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
46486cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
46496cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
46507a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
46517a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
46527a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
46537a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
465494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            contactValues.put(RawContacts.DELETED, c.getLong(COLUMN_DELETED));
465538446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey            contactValues.put(RawContacts.CONTACT_ID, c.getLong(COLUMN_CONTACT_ID));
4656c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            contactValues.put(RawContacts.STARRED, c.getLong(COLUMN_STARRED));
46577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
46587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
46607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
46617a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
46627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
46637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
466423c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                if (c.isNull(COLUMN_CONTACT_ID)) {
466523c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                    continue;
466623c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                }
46677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
46687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
466923c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data._ID, c.getLong(COLUMN_DATA_ID));
46707a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
46717a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
467223c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_PRIMARY, c.getLong(COLUMN_IS_PRIMARY));
467323c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_SUPER_PRIMARY, c.getLong(COLUMN_IS_SUPER_PRIMARY));
46747a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
46759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
46769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
46779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
46789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
46797a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
46807a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                for (int i = 0; i < DATA_KEYS.length; i++) {
46817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
46827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
46837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
46847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
46857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
46867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
46877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
46887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
46897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
46907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
46917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
46927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
46937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
46947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
46957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
46967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
46977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
46997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
47007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
47017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4702226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    /**
4703226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
4704226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
4705226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     */
4706226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    private static class GroupsEntityIterator implements EntityIterator {
4707226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private final Cursor mEntityCursor;
4708226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private volatile boolean mIsClosed;
4709226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4710226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final String[] PROJECTION = new String[]{
4711226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups._ID,
4712226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_NAME,
4713226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_TYPE,
4714226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.SOURCE_ID,
4715226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.DIRTY,
4716226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.VERSION,
4717226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.RES_PACKAGE,
4718226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE,
4719226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE_RES,
47207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.GROUP_VISIBLE,
47217a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC1,
47227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC2,
47237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC3,
47247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC4,
47257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYSTEM_ID,
472694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.NOTES,
47271a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.DELETED,
47281a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.SHOULD_SYNC};
4729226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4730226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ID = 0;
4731226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_NAME = 1;
4732226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 2;
4733226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_SOURCE_ID = 3;
4734226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_DIRTY = 4;
4735226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_VERSION = 5;
4736226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_RES_PACKAGE = 6;
4737226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE = 7;
4738226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE_RES = 8;
4739226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_GROUP_VISIBLE = 9;
47407a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 10;
47417a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 11;
47427a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 12;
47437a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 13;
47447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYSTEM_ID = 14;
47457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_NOTES = 15;
474694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        private static final int COLUMN_DELETED = 16;
47471a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        private static final int COLUMN_SHOULD_SYNC = 17;
4748226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4749226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
4750226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
4751226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = false;
4752226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4753226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String updatedSortOrder = (sortOrder == null)
4754226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    ? Groups._ID
4755226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    : (Groups._ID + "," + sortOrder);
4756226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4757b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            final SQLiteDatabase db = provider.mDbHelper.getReadableDatabase();
4758226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4759b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            qb.setTables(provider.mDbHelper.getGroupView());
4760226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setProjectionMap(sGroupsProjectionMap);
4761226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (groupIdString != null) {
4762226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups._ID + "=" + groupIdString);
4763226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4764f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            final String accountName = getQueryParameter(uri, Groups.ACCOUNT_NAME);
4765f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            final String accountType = getQueryParameter(uri, Groups.ACCOUNT_TYPE);
4766226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!TextUtils.isEmpty(accountName)) {
4767226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups.ACCOUNT_NAME + "="
4768226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4769226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + Groups.ACCOUNT_TYPE + "="
4770226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
4771226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4772226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
4773226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    null, null, updatedSortOrder);
4774226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToFirst();
4775226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4776226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4777226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public void close() {
4778226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4779226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("closing when already closed");
4780226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4781226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = true;
4782226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.close();
4783226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4784226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4785226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public boolean hasNext() throws RemoteException {
4786226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4787226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
4788226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4789226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4790226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return !mEntityCursor.isAfterLast();
4791226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4792226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4793038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4794038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4795038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4796038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4797038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4798038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4799e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4800226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public Entity next() throws RemoteException {
4801226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4802226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
4803226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4804226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!hasNext()) {
4805226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
4806226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4807226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4808226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
4809226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4810226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final long groupId = c.getLong(COLUMN_ID);
4811226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4812226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            // we expect the cursor is already at the row we need to read from
4813226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            ContentValues groupValues = new ContentValues();
4814226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4815226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4816226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups._ID, groupId);
4817226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
4818226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
4819226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4820226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4821226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
4822226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
4823226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
48247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
48257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
48267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
48277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
48287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
482994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            groupValues.put(Groups.DELETED, c.getLong(COLUMN_DELETED));
48307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
48311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            groupValues.put(Groups.SHOULD_SYNC, c.getString(COLUMN_SHOULD_SYNC));
4832226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            Entity group = new Entity(groupValues);
4833226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4834226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToNext();
4835226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4836226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return group;
4837226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4838226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    }
4839226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4840a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
48417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
48427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
4843568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
4844568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
48457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
48467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
48475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS:
48485ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID:
48497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
48505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (match == RAW_CONTACTS_ID) {
48517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
48527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
48537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
485446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                return new RawContactsEntityIterator(this, uri, contactsIdString,
485546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                        selection, selectionArgs, sortOrder);
4856226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS:
4857226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS_ID:
4858226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String idString = null;
4859226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                if (match == GROUPS_ID) {
4860226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    idString = uri.getPathSegments().get(1);
4861226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                }
4862226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4863226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                return new GroupsEntityIterator(this, idString,
4864226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        uri, selection, selectionArgs, sortOrder);
48657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
48667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
48677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
48687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
48697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
48704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
48714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4872a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
48734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4874b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4875b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP:
4876be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
4877b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4878b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4879b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4880f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
4881f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4882b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4883be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4884b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4885b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4886508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4887b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
488848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
488948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
489048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
489148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
489248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
489348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
489448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
489548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
489648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
489748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
489848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
489948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4900b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4901b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4902b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4903b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4904b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4905b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4906b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4907b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4908c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4909c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4910c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4911c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
491261efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
491361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
49144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
49154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
49167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
491725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
49183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
491925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
49203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
492125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindNull(1);
49223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
492325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
492425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
492525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
49263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
49273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
492873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
492973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
493073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
493173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
4932a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
493373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
493473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4935c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4936c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
4937c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
4938c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4939c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4940c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4941653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
4942c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
4943653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
4944653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
4945c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
4946c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4947c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
4948c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4949c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
4950c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
4951c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4952c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4953c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4954653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
4955c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
4956653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
4957653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
4958c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
4959c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4960ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4961f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
4962f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
4963f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4964f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4965f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4966f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
4967f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokens.length == 0) {
4968f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4969f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4970f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4971f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String address = tokens[0].getAddress();
4972f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int at = address.indexOf('@');
4973f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (at != -1) {
4974f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            address = address.substring(0, at);
4975f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4976f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4977f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4978f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
4979f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4980f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4981f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4982f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
4983f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4984f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
4985f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
4986f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4987f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4988f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4989f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4990f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
4991f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4992f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4993a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
4994a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
4995a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
4996a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4997a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
4998a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4999a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
5000a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
5001a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
5002a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
5003a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
5004f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5005f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
5006f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
5007f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5008f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5009315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private interface NicknameLookupPreloadQuery {
5010315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
5011315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5012315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String[] COLUMNS = new String[] {
5013315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            NicknameLookupColumns.NAME
5014315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        };
5015315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5016315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int NAME = 0;
5017315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
5018315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5019315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    /**
5020315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * Read all known common nicknames from the database and populate a Bloom
5021315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * filter using the corresponding hash codes. The idea is to eliminate most
5022315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * of unnecessary database lookups for nicknames. Given a name, we will take
50233684089aba82df3f7a0c111e7c96ed8b0380e57aDmitri Plotnikov     * its hash code and see if it is set in the Bloom filter. If not, we will know
5024315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * that the name is not in the database. If it is, we still need to run a
5025315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * query.
5026315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * <p>
5027315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * Given the size of the filter and the expected size of the nickname table,
5028315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * we should expect the combination of the Bloom filter and cache will
5029315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     * prevent around 98-99% of unnecessary queries from running.
5030315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov     */
5031315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private void preloadNicknameBloomFilter() {
5032315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        mNicknameBloomFilter = new BitSet(NICKNAME_BLOOM_FILTER_SIZE + 1);
5033315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5034315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupPreloadQuery.TABLE,
5035315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                NicknameLookupPreloadQuery.COLUMNS,
5036315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                null, null, null, null, null);
5037315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        try {
5038315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            int count = cursor.getCount();
5039315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            for (int i = 0; i < count; i++) {
5040315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                cursor.moveToNext();
5041315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                String normalizedName = cursor.getString(NicknameLookupPreloadQuery.NAME);
5042315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                int hashCode = normalizedName.hashCode();
5043315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov                mNicknameBloomFilter.set(hashCode & NICKNAME_BLOOM_FILTER_SIZE);
5044315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            }
5045315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        } finally {
5046315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            cursor.close();
5047315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        }
5048315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
5049315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5050315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5051f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5052f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
5053f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5054f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
5055315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int hashCode = normalizedName.hashCode();
5056315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        if (!mNicknameBloomFilter.get(hashCode & NICKNAME_BLOOM_FILTER_SIZE)) {
5057315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            return null;
5058315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        }
5059315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5060f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SoftReference<String[]> ref;
5061f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
5062f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        synchronized (mNicknameClusterCache) {
5063f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
5064f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
5065f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                if (ref == null) {
5066f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    return null;
5067f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
5068f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = ref.get();
5069f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
5070f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5071f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5072f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (clusters == null) {
5073f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
5074f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
5075f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
5076f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
5077f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
5078f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5079f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
5080f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5081f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5082315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    private interface NicknameLookupQuery {
5083315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
5084315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5085315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        String[] COLUMNS = new String[] {
5086315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
5087315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        };
5088315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5089315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov        int CLUSTER = 0;
5090315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov    }
5091315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
5092f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
5093b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5094f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
5095f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
5096f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
5097f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                null, null, null);
5098f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
5099f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
5100f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (count > 0) {
5101f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = new String[count];
5102f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int i = 0; i < count; i++) {
5103f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    cursor.moveToNext();
5104f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
5105f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
5106f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
5107f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        } finally {
5108f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            cursor.close();
5109f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5110f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
5111f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5112f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5113f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
5114f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5115f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
5116f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
5117f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5118f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5119f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5120f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5121f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
5122f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
5123f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5124f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5125f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5126f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5127f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
5128f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5129f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5130f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5131f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5132f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
5133f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5134f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
5135f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
5136f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
5137f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
5138f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
5139f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
5140f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5141f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5142f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5143f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
5144f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5145f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
5146f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
5147f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
5148f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5149f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
51502d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5151d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5152d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5153d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5154d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
5155d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5156d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5157d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5158e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5159d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN("
5160d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
5161d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
5162d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NICKNAME + ","
5163d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.ORGANIZATION + "))");
5164e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5165e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
51665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
5167c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5168c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
5169c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
5170c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5171c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5172a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
5173c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            String limit) {
517420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), limit,
517520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                true);
51765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
51775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
51785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
517920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            String limit, boolean allowEmailMatch) {
5180d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5181d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + NameLookupColumns.RAW_CONTACT_ID +
5182d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
5183d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
5184d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
51855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
5186a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
5187a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
5188a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
518920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                + NameLookupType.ORGANIZATION);
519020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
519120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
519220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
519320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        sb.append(")");
51943de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
5195c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limit != null) {
5196c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
5197c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5198c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(")");
5199ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
5200ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
52014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
52024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
52034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
52044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5205b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5206b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5207b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5208b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5209b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
52104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
52114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5212b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5213b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5214b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5215caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
52165e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
52175e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
52185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
52195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
52205e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
52215e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
52225e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
52235e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
52245e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
52255e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
52265e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5227caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5228caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5229caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
5230df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
5231df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
5232caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5233caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5234caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5235caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
52366f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5237caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
52386f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5239caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5240f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5241627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    protected boolean isWritableAccount(Account account) {
5242627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5243627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5244627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5245627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
5246627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        account.type.equals(sync.accountType)) {
5247627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    return sync.supportsUploading();
5248627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5249627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5250627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5251627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5252627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
5253627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        return false;
5254627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5255b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5256f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5257f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5258f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5259f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5260f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5261f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5262f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5263f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5264f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5265f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5266f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5267f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5268f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5269f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5270f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5271f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5272f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5273f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5274f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5275f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5276f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5277f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5278f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5279f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5280f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5281f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5282f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5283f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5284f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5285f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5286f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5287f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5288f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5289f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5291f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5293f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5294f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5295f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5296f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5297f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5298f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5299f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5300f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5301f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5302f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5303f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5304f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5305f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5306f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5307f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5308f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5309f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5310f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5311f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5312f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5313f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5314f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5315f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5316f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5317f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5318f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5319f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5320f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5321f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5322f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5323f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
53244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5325