ContactsProvider2.java revision 5e28b3a5e44bf4f2c0980c50a2ab35350fc5f230
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;
56568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
5867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
593de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.content.SharedPreferences.Editor;
60b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
62ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
63a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
64b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
65b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
68c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
706ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
71d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
72b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
730e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
74d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.pim.vcard.VCardComposer;
753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
76508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
773de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
783de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.LiveFolders;
793de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.OpenableColumns;
803de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.SyncStateContract;
81b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
823de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
833de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
843de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
863de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
873de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
8882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
90ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
91ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
95de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
96b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
974097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
99a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
100a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
101f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Token;
102f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Tokenizer;
103c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
105d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
106b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
107d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
108d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
109f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport java.lang.ref.SoftReference;
1107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
112b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1130e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
116b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1170e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
118ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1245b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
125caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
126bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
127bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
128bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
130619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
131619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
132619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
1373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * Shared preference key for the legacy contact import version. The need for a version
1383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
1413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final String PREF_CONTACTS_IMPORTED = "contacts_imported_v1";
1423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int PREF_CONTACTS_IMPORT_VERSION = 1;
1433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1440e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1450e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
146a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1485e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final String TIMES_CONTACED_SORT_COLUMN = "times_contacted_sort";
1495e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
150d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
1515e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            + TIMES_CONTACED_SORT_COLUMN + " DESC, "
1529b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
153d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
154d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
155d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
156d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
157d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
158d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
167f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
1684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
17246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
1734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
176ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
17748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
17848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
17948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
18048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
18148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
18248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
18348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
18448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
185a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
188b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
189b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
190b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
19182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
19282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
1931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
19431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
19531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
196eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
197eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
198ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
199ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
200ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
201ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
20235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
203b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
20435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
205c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
206c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
207c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2081b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2091b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
21346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
21446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
21567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface ContactsQuery {
2165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final String TABLE = Tables.RAW_CONTACTS;
2179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
21867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2196cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2206cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
2216cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
222ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
223ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2245ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
22567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_NAME = 1;
22667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_TYPE = 2;
22767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
22867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
229d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
230f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
231f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
232f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
23367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
23467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2356cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
237f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
238ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
239ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
240d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
24167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
242d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
243ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DisplayNameQuery {
24667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
251f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
252a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            Organization.TITLE,
2533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 0;
2563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 1;
257a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        public static final int DATA = 2;
258a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        public static final int TITLE = 3;
2593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
26267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
269f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
27088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
27188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
27288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
27388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
27488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
27588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
27688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
277f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
284f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
288321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
28920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
29020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
291321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
292321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
29320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
29420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
295f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
296f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private interface NicknameLookupQuery {
297f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
298f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
299f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] COLUMNS = new String[] {
300f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
301f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        };
302f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
303f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int CLUSTER = 0;
304f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
305f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
30619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
30719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
30819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
30919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
31019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                ContactsContract.RawContacts.DELETED
31119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
31219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
31319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
31419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
31519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
31625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private static final HashMap<String, Integer> sDisplayNameSources;
3173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    static {
31825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources = new HashMap<String, Integer>();
31925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(StructuredName.CONTENT_ITEM_TYPE,
32025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.STRUCTURED_NAME);
321a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sDisplayNameSources.put(Nickname.CONTENT_ITEM_TYPE,
322a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                DisplayNameSources.NICKNAME);
32325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Organization.CONTENT_ITEM_TYPE,
32425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.ORGANIZATION);
32525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Phone.CONTENT_ITEM_TYPE,
32625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.PHONE);
32725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Email.CONTENT_ITEM_TYPE,
32825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.EMAIL);
3293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
33031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
331c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
332df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
333caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
33471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
33571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
33671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
33771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
33871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
33971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
34071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
34171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
34271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
34371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
34471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
34571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
34671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
34771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
348038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
349038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
350e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
3525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
3535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentStarredProjectionMap;
3545e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentFrequentProjectionMap;
355f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
356f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final HashMap<String, String> sContactsVCardProjectionMap;
357ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
358d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
35946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    /** Contains the columns from the raw contacts entity view*/
36046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final HashMap<String, String> sRawContactsEntityProjectionMap;
3614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
366e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
367ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
368ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
370ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
371373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
372b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
373eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
374eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
37582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
37682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final HashMap<String, String> sStatusUpdatesProjectionMap;
3771b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
380c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
381c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
383c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
384ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    /** Precompiled sql statement for incrementing times contacted for a contact */
385ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    private SQLiteStatement mContactsLastTimeContactedUpdate;
3863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
38725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
38873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /** Precompiled sql statement for marking a raw contact as dirty */
38973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private SQLiteStatement mRawContactDirtyUpdate;
39082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
391a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
392f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
393f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
394a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
395a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
396a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
3970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
398a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
399a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
400f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
401f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
402f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
403f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
4044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
4054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
406a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
407d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
408d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
409d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
4103653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
4113653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4122d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
4132d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4143653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
4155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
4165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
4175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
418f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
4195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
420ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
421ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
4225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
4233653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4245ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
4255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
4265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
42746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
42846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
42946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
430b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
4314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
4324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
433ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
43448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
4355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
436ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
4374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
43848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
4395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
4405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
4414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
442ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
44348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
4441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
445ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
446ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
447ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
448ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
44935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
450b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
451b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
45235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
453a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
454b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
455b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
456b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
457b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
459eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
460eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
46182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
46282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
4631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
464c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
465c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
466c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
467c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
468c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
469c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
470c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
4721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
4731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
4741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
4751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
4761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
4771b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
4781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
47919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
48019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
48119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
482038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
483038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
484e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
4864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
4874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
4884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
4894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
4904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
4914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
4924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
4934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
494f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
4954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
4965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
497f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
4983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
4993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_PRESENCE,
5003296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
5013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS,
5023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
5033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
5043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
5053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
5063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
5073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_LABEL,
5083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
5093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_ICON,
5103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
5113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
5125e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5135e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5145e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Long.MAX_VALUE + " AS " + TIMES_CONTACED_SORT_COLUMN);
5155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5165e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5175e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Contacts.TIMES_CONTACTED + " AS " + TIMES_CONTACED_SORT_COLUMN);
5195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
520f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap = Maps.newHashMap();
521f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
522d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
523f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.SIZE, "0 AS " + OpenableColumns.SIZE);
5244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
5334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
5344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
5354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
5364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
5374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
5384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
5394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
5404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
5414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
5424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
5434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
5444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
5452815f58f72f109790585931f601a63ddc02536a5Evan Millar
5464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
5474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
5484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
5494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
5504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
5514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
5524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
5534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
5544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
5554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
5564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
5574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
5584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
5594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
5604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
5614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
5624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
5634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
5644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
5654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
5664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
5674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
5684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
5694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
57382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sDataProjectionMap.put(Data.CONTACT_ID, Data.CONTACT_ID);
5744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
57956d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
587a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
5884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
589a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
59046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        HashMap<String, String> columns;
59146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns = new HashMap<String, String>();
59246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts._ID, RawContacts._ID);
59346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
59446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
59546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
59646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
59746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.VERSION, RawContacts.VERSION);
59846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DIRTY, RawContacts.DIRTY);
59946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DELETED, RawContacts.DELETED);
60046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
60146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
60246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
60346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
60446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
60546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.MIMETYPE, Data.MIMETYPE);
60646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA1, Data.DATA1);
60746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA2, Data.DATA2);
60846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA3, Data.DATA3);
60946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA4, Data.DATA4);
61046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA5, Data.DATA5);
61146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA6, Data.DATA6);
61246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA7, Data.DATA7);
61346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA8, Data.DATA8);
61446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA9, Data.DATA9);
61546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA10, Data.DATA10);
61646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA11, Data.DATA11);
61746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA12, Data.DATA12);
61846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA13, Data.DATA13);
61946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA14, Data.DATA14);
62046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA15, Data.DATA15);
62146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC1, Data.SYNC1);
62246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC2, Data.SYNC2);
62346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC3, Data.SYNC3);
62446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC4, Data.SYNC4);
62546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
62646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.STARRED, Data.STARRED);
62746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
62846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
62946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
63046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
63146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        sRawContactsEntityProjectionMap = columns;
63246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
6333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
6353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
6373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6393296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6403296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
6453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
6463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6473296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
6483296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.PRESENCE,
6493296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
6503296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS,
6513296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
6523296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
6533296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6543296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
6553296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6563296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
6573296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6583296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_ICON,
6593296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
6603296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
6625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
6635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
6645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
6655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
6665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
6675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
6685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
6705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
6715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
6725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
6735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
6745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
6755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
6765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
6775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
6785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
6795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
6805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
6815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
6825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
6835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
6845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
6855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
6865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
6875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
6885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
6895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
6908f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
6915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
6935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
6945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
6955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
6965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
6975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
698a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
6995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
7005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
7015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
7023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
7033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
7043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
7053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
7063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
7073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
7083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
7103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
7123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
7143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
7153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
7173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
7183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
7193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS,
7203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
7213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
7223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
7243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
7263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
7283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
7293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
730e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
731e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
732e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
73356d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
73456d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar                Contacts.LOOKUP_KEY + " AS " + PhoneLookup.LOOKUP_KEY);
735e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
736e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
737e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
738e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
739e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
740e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
741e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
742e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
743e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
744e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
745e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
746e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
747e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
748e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
749e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
750e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
751e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
752e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
753e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
754e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
755e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
756e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
757e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
758e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
759e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
760e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
7619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
762ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
763ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
76489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
765035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
766035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
7679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
7689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
7699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
77089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
771ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
77267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
773ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
7743cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
77594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
7763cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
77738446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
77889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
77989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
78089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
78189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
782ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
783ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
7846cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
785ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
787d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
788d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
789ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
790ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
791ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
792d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
793d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
794ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
795f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
796ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
797ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
798b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
799b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
800b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
801b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
8020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
8030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
804b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
805b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
806eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
807eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
808eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
809eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
810eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
811eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
812341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
813341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
814341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
815fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
816341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
817341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
818341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
81968936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
82068936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
82168936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
82268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
82368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
824e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
82568936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
82668936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
82768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
828eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
829eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
830373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
8314dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
8320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
8330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
83482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
83582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
83682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
83770c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
83870c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
83982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
84082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
84182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
84282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
8430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
8440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
8450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
8460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
8470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
84882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sStatusUpdatesProjectionMap = columns;
84919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
8501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
8511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
8521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
8531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
8541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
8551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
8561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
8571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
8581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
8591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
8601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
8614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
8624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
8633296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
8643296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        map.put(toField, fromField + " AS " + toField);
8653296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    }
8663296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
8673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
8683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
8693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
8703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
8713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
873653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
8743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
8763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
877a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
878a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
879a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
880a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
881a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
882a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
883a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
884a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
8853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
887653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
888653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
889b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
890653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
891653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
892653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
893653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
8943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
8963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
8975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
898e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
899e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
900e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
901e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
902653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
903e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
904e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
905e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
9063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
9103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
9113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
912653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
913f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
91414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
91514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
916653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
917653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
918653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
919653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
920653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
921653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
922653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
923653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
924653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
925653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
926653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
927653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
928653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
929653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
930653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
931653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
932653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
933653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " = " + dataId, null);
934653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
935653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
936f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
937653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
938653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
9393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
94214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
94314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
94414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
9453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
9463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
9473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
9485ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
9493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
9513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
9545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, rawContactId);
9553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
95614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                setIsPrimary(rawContactId, newPrimaryId, getMimeTypeId());
9573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long rawContactId) {
961e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
962e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
9635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Cursor c = queryData(db, rawContactId);
9643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
965e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
96614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
967f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
968e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
969e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
970e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
971e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
9723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
9733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
9743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
9753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
976e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
977e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
978e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
979e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
980e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
981e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
982e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
983e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
984e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
9853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long rawContactId) {
98814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return db.query(DataDeleteQuery.TABLE, DataDeleteQuery.CONCRETE_COLUMNS,
98914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId +
99014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
9913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
9923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
99425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
9953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String bestDisplayName = null;
99625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
99725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
99867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Cursor c = db.query(DisplayNameQuery.TABLE, DisplayNameQuery.COLUMNS,
9995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId, null, null, null, null);
10003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
10013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                while (c.moveToNext()) {
10023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String mimeType = c.getString(DisplayNameQuery.MIMETYPE);
1003a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1004a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    // Display name is at DATA1 in all type.  This is ensured in the constructor.
1005a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    String name = c.getString(DisplayNameQuery.DATA);
1006a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    if (TextUtils.isEmpty(name)
1007a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                            && Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
1008a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        name = c.getString(DisplayNameQuery.TITLE);
10093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
1010a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    boolean primary = StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
1011a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        || (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
10123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
101301911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                    if (name != null) {
101425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        Integer source = sDisplayNameSources.get(mimeType);
101501911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                        if (source != null
101601911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                && (source > bestDisplayNameSource
101701911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                        || (source == bestDisplayNameSource && primary))) {
101825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                            bestDisplayNameSource = source;
10193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            bestDisplayName = name;
10203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        }
10213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
10223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
10233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
10253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
10263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
102825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
1029285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1030285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateDisplayName(db, rawContactId);
1031285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
10323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1033a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1034a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1035a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return true;
1036a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1037622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1038622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1039622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1040622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
1041622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1042622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1043622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1044622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
1045622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=" + dataId,
1046622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    null, null, null, null);
1047622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1048622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1049622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1050622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1051622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        values.put(key, cursor.getString(i));
1052622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1053622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1054622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1055622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1056622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1058622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1059622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
10633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
10653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1070622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
10713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1072622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
10733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1074622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
10753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10785ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1079622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
108014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
108114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
108214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1083f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1084f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
108525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
108614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
108714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
108814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
108914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
109014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1091f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1092622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1093622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1094cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1095622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1096622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
109714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1098f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
109914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1100f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
1101f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
1102f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
1103f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
110414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
110525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
110614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
110714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
110814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
110914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
111014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
111114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
111214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
111314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
111414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1115f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
111625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
111714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
11183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1121622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
11223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1123622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1124622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1125622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1126622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
11273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1128622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1130622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1131622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1132622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1133622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1134622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
113567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1136622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
113767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
113867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1139622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1140622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
11418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1142622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1143622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
114467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
114567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
114667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
114767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
114867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
114967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
11508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1151622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
1152622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(name);
1153622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
1154622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1155622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1156622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1157622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1158622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1159622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1160622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1161622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1162622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1163622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1164622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1165622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1166622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1167622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1169622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1170622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1171622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1172622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1173622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1174f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1175622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1176622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1177622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1178f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1179622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1180622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1181622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1182622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1183622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1184622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1185622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1186622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1187622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1188622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1189622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1190622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1192622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1193622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1194622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1195622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1196622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
119767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
119867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
119967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
120067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1201622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1202622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1203622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1204622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1205622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1206622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
120767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
120867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
120967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1210622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1211622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1212622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
12133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
12183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
12203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
12213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
12233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
12243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
12253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
12263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1230622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1231622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1232622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
12333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1234622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1235622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1236f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1237622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1238622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1239622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1240f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1241622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
12423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1243622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1244622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1245622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1246622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1247622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1248622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1249622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1250622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
12513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1252622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1253622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1254622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1255622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1256622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
12573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
12613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
12633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
12643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1268a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1269a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1270a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1271a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1272a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
127325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1274a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1275a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
12763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
127914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1280f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1281a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1282a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1283a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
128414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
128514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1286f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
128714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
128825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1289a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
1290a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
129114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
129214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
129414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1295a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
129614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
129714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
129925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1300a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
130114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
130214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
130314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
130414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
13053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
13063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
13073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
13083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
13093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
13103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
13113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
13123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1313a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1314a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1315a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1316a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1317a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
13183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1320e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1321e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1322e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1323e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1324e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1325e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1326e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
13275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
132814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
132914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
133114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1333f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
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 address = values.getAsString(Email.DATA);
134314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1344f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
134514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1346f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1347f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
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;
1361e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1362e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1363e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1364e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1365e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1366e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1367e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1368e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1369e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1370e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1371e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1372e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1373e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1374e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
137514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
137614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
137714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
137814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
137914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
138014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
138114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
138214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
138314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
138414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
138514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
138614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
138725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1388f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
138914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
139014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
139114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
139314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1394f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
139514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
139614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
139714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
139814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1399f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
140014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1401f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1402f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
140325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
140414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
140514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
140614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
140714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
140814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
140914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
141014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
141214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1413f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
141425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
141514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
141614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
141714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
141814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
14193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
14203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
14223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
14233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
14265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
14270b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
14280b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
14290b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
14300b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1431653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
14320b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1433653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
14340b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1435285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
143625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
14370b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
14380b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
14390b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1440653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1441653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1442653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1443653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1444653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1445f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
144614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
144714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
14480b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
14490b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
14500b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1451653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1452f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
1453653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
14540b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1455285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
145625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
14570b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1458f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
14590b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
146014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
146114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
146314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
146414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
146514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
146614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
146814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1470285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
147125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
147214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1473653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1474653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1475653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1476e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1477e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1478e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1479e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1480653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1481653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1482653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1483e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1484653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1485653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1486e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1487653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
14885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1489653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1490e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1491653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1492653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
1493653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=" + dataId, null);
1494e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
14953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
14983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
15003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
15013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
15023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
15033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
15043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
15053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
15063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
15073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
15083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
15093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
15103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
15123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1513653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1514653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1515653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1516653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1517653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1518653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1519653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1520653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1521653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
15220be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
15230be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
15240be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1525653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1526653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1527653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1528653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1529f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
153014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1531653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1532f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
15330be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
15340be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
15350be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
15360be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
15370be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
15380be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
15390be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
15400be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
15410be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
15420be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
15430be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
15440be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1545b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
15460be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1547b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
15480be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1549653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1550653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1551653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1552653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1553653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1554653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1555653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1556653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1557653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1558653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1559653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1560653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1561653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1562653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1563653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1564653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1565653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1566653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1567653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1568653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1569653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1570653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1571653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1572653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1573653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId);
1574653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1575653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1576653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1577653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1578a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1579a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1580a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1581a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1582a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1583653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1584653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1585a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1586a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1587a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1588a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1589a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1590a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1591a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1592a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1593a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1594285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1595285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1596285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1597a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1598a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1599a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1600a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1601a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1602f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1603a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1604f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1605a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1606a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1607a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1608a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1609a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1610a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1611a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1612a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1613a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1614a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1615a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1616a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1617a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1618a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1619a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1620a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1621a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1622a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
16233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
162453056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
1625b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
162631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
16274097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1628f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1629f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache =
1630f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            new HashMap<String, SoftReference<String[]>>();
1631622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1632622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1633622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1634f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1635a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1636a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
163720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
163820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1639ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
164073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private boolean mImportMode;
164173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1642b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mInsertedRawContacts = Sets.newHashSet();
1643b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1644b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1645de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
16461a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
16471a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
164881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
164981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
1650a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
165153056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
1652a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1653a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1654a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1655a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
1656a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
165753056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
165853056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
1659a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
16604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
16614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
16624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1663de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
166435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1665de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1666b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1667a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1668b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1669b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper, mAggregationScheduler);
16700e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1671a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1672b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1673653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1674c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1675653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1676653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1677653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1678653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1679653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1680c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1681653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1682653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1683653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1684653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1685653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1686653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1687653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1688653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1689653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1690653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1691653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1692ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate = db.compileStatement(
1693ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "UPDATE " + Tables.CONTACTS +
1694ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
1695ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "WHERE " + Contacts._ID + "=?");
1696a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
169725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate = db.compileStatement(
169825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
169925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
170025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
170125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
17023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
170373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
170473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                + RawContacts.DIRTY + "=1 WHERE " + RawContacts._ID + "=?");
170573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1706a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mLastStatusUpdate = db.compileStatement(
1707a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.CONTACTS
1708a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                + " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1709a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1710a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1711a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1712a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1713a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1714a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1715a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1716a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1717a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
17180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
17190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1720a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " LIMIT 1)"
1721a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                + " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1722e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1723622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        final Locale locale = Locale.getDefault();
172428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
172528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
172628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
172728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
1728622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1729622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                locale);
1730f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1731622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
17324097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1733f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1734f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1735f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1736f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1737f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1738f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1739f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1740a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateInsert = db.compileStatement(
1741a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1742a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1748a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1749a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateReplace = db.compileStatement(
1750a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1751a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
17530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1758a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1759a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateAutoTimestamp = db.compileStatement(
1760a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
17620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1763a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
17640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
17650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
17660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mStatusAttributionUpdate = db.compileStatement(
17670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
17690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
17700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
17710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1772a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1773a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateDelete = db.compileStatement(
1774a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1775a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1776a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
17773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
17783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1779e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
17803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
17813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
178267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
178367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
17843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
17853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
178614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
17873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
17883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1789622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1790622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1791a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1792a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
17933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
17943d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1795568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            importLegacyContactsAsync();
17963d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1797568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1798c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        verifyAccounts();
179970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
1800f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1801f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
18021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
18034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
18044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1805c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
1806c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1807c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1808c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
1809c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
181031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1811de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1812b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1813b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
181431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
181531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1816285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    /* package */ ContactAggregationScheduler getContactAggregationScheduler() {
1817285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mAggregationScheduler;
1818285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1819285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1820013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1821013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1822013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1823013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18243d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18253d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18263d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
18273d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18283d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1829568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1830568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1831568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1832568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1833568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1834568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
1835568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
1836568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1837568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
1838ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        mAccessLatch = new CountDownLatch(1);
1839568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1840568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
1841568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
1842568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
1843568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                if (importLegacyContacts()) {
1844568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1845568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    /*
1846568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * When the import process is done, we can unlock the provider and
1847568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * start aggregating the imported contacts asynchronously.
1848568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     */
1849ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch.countDown();
1850ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1851568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    scheduleContactAggregation();
1852568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
1853568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1854568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
1855568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1856568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
1857568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1858568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18593d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
1860568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1861568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        if (importLegacyContacts(importer)) {
18623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
18643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
18653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
18663d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
18683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
18693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
18723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1873568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
18740e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
18753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
187673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mImportMode = true;
18773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
18783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
18790e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff            mContactAggregator.setEnabled(aggregatorEnabled);
18803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
18823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
18833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
188473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } finally {
188573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mImportMode = false;
18863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18873d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18883d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1889a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
1890a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
1891a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
1892a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
1893a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
1894a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1895a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
1896a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1897a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1898a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1899a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1900a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1901a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1902b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1903a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1904a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1905568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1906568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1907568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1908568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1909568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1910568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1911568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1912568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
1913ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
1914ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
1915ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
1916ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
1917ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
1918ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1919ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
1920ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
192181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
1922ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
1923ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1924568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1925568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1926568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1927568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1928568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1929568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1930568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1931568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1932568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1933568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1934568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1935568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1936568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1937568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1938568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1939568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1940568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
1941568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1942568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1943568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1944568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1945568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1946568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1947568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
1948568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1949568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1950568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1951568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1953285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1954bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1955b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1956b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1957285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
19581ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1959b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1960b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1961b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1962b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
1963285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
1964b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
1965df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
1966285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1967285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1968285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1969285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
1970bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1971b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1972b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1973285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1974b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
19751ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
19761a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
19771a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1978b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
19791a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
1980b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1981b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1982b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1983bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1984b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1985b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1986b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long rawContactId : mInsertedRawContacts) {
1987b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mContactAggregator.insertContact(mDb, rawContactId);
1988285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1989b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1990b5a4add17815167d20a90645779df34cdf45280dFred Quintana        String ids;
1991b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
1992b5a4add17815167d20a90645779df34cdf45280dFred Quintana            ids = buildIdsString(mUpdatedRawContacts);
1993b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mDb.execSQL("UPDATE raw_contacts SET version = version + 1 WHERE _id in " + ids,
1994b5a4add17815167d20a90645779df34cdf45280dFred Quintana                    new Object[]{});
1995b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1996b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1997b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
1998b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
1999b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().update(mDb, id, entry.getValue());
2000b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2001b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2002b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2003b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2004b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2005b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private String buildIdsString(HashSet<Long> ids) {
2006b5a4add17815167d20a90645779df34cdf45280dFred Quintana        StringBuilder idsBuilder = null;
2007b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2008b5a4add17815167d20a90645779df34cdf45280dFred Quintana            if (idsBuilder == null) {
2009b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder = new StringBuilder();
2010b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append("(");
2011b5a4add17815167d20a90645779df34cdf45280dFred Quintana            } else {
2012b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append(",");
2013b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
2014b5a4add17815167d20a90645779df34cdf45280dFred Quintana            idsBuilder.append(id);
2015b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2016b5a4add17815167d20a90645779df34cdf45280dFred Quintana        idsBuilder.append(")");
2017b5a4add17815167d20a90645779df34cdf45280dFred Quintana        return idsBuilder.toString();
2018285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2019285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2020285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2021cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
202281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
202381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
202481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
202581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
202681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
202781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
202881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2029cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2030568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2031568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected void scheduleContactAggregation() {
2032dee54bb86f3608730f0b9f37d8982a7f6b280a85Dmitri Plotnikov        mContactAggregator.schedule();
2033568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2034568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2035285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2036285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mInsertedRawContacts.contains(rawContactId);
2037285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2038285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
20393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
20403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
20413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
20423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
20433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
20443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
20453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
20463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
20473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
20484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2049de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2050bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2051b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "insertInTransaction: " + uri);
2052b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2053f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2054f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2055f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2056f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2057a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2058a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
205935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2060a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
206135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2062b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
206335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
206435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2065d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2066d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
20676bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20686bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
20696bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
20705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2071f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
2072d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                id = insertRawContact(values, account);
2073f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2074a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2075a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2076a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
20785ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2079f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2080f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2081a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2082a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2083a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2084a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2085f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2086f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2087a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2088a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2089a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2090ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2091ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
20925aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertGroup(uri, values, account, callerIsSyncAdapter);
2093f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2094ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2095ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2096ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2097eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
20985aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
209943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2100eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2101eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2102eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
210382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
210482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
21051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
21061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
21071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2108a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
210981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2110f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2111a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2112a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
21147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
21157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2117de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2118a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2119a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2120a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2121035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
2122035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
2123035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
2124035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
2125035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
21267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2127035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
2128035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
21296cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
21306cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2131035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
2132035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
2133035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
2134035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
2135035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
2136035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
2137035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2138035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
2139df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_NAME, account.name);
2140df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_TYPE, account.type);
2141035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2142035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
21437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
21447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2146d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
21476bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
21486bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
21496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
21506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2151d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2152de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
21536bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
21546bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2156a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2157a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2158a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2159f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
2160a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2161a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2162d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertRawContact(ContentValues values, Account account) {
2163a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
2164d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        overriddenValues.putNull(RawContacts.CONTACT_ID);
2165f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
21667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
21677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
21703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
21713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            overriddenValues.put(RawContacts.AGGREGATION_MODE,
21723d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                    RawContacts.AGGREGATION_MODE_DISABLED);
21733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
21743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2175023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        long rawContactId =
2176023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov                mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, overriddenValues);
2177023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
2178285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2179285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2180285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.add(rawContactId);
2181023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2182a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2183a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2184a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2185a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2186a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2187a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2188a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2189a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2190f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2191a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2192de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2193de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
219467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2195de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
219620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2197de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2198de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2199de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2200b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2201de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2202de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2203508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2204de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2205de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2206de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2207de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2208de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
22094097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2210b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2211de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2212a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2213a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2214a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2215f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2216de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2217a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2218b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2219a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2220a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2221a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2222a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2223a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
22244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
22254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22268e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
22278e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
22288e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
22298e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
22308e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2231b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2232f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
22338e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
22348e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
22358e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22368e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2237421782cb554e5050cf62a86b98df6520038dcd15Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId);
2238f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22398e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
22408e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22418e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2242b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2243f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
22448e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
22458e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
22468e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2247f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22488e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2249f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2250c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2251b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
22528e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2253f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22548e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2255f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2256f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2257f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2258a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
22595ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
22609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
22619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
22625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
22639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
22649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
22659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
22669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
22679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
22685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId) {
22699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
22706cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, RawContacts._ID + "="
22715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                + rawContactId, null, null, null, null);
22729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
22739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
227467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountName = c.getString(ContactsQuery.ACCOUNT_NAME);
227567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountType = c.getString(ContactsQuery.ACCOUNT_TYPE);
22769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
22779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
22789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
22799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
22809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
22819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
22829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
22839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
22849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
22859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
22869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
22879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
22889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
22899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
22905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
22916cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
22929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2293df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
22949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
22959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
22969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
22979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
22989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2299df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2300df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
23019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
23029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
23039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
23049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
23059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
23069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
23079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
23089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
23099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
23109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
23119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
23129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
23139261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
23149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
231520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
231620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2317f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
231820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
231920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2320de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2321de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
232214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2323de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2324de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
232514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
232614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2327a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2328a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2329f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
233088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
2331a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    if (rowHandler.isAggregationRequired()) {
2332a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        triggerAggregation(rawContactId);
2333a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    }
233488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
233520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
233620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2337de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
233820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
233920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
234020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
234120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
234220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
234388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
234488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
234588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
234620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2347f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
234888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
234988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
235014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=" + dataId, null,
235114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                null);
2352f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
235320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
235420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
235520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
235620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
235720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
235814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
235920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
236020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
236120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
236220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
236320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
236420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
236520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
236620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
236720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
23687a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
236920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
237020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
237120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2372a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2373a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = rowHandler.delete(mDb, c);
23748e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2375a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            if (rowHandler.isAggregationRequired()) {
2376a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                triggerAggregation(rawContactId);
2377a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            }
23788e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return count;
237920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
238020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
238120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
238220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
238320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
238420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2385ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2386ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2387b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, Account account,
2388b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            boolean callerIsSyncAdapter) {
2389ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
2390ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
2391ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
2392ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2393ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2394ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
239567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE);
239667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2397b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            overriddenValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
239867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
239967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        overriddenValues.remove(Groups.RES_PACKAGE);
2400ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2401f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
240273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            overriddenValues.put(Groups.DIRTY, 1);
240373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
240473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2405ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
2406ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
24071a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (overriddenValues.containsKey(Groups.GROUP_VISIBLE)) {
24081a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2409ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2410ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2411ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2412ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2413ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
24145aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2415e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
24165aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
24171a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
24181a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2419e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
24201a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2421e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2422e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2423e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
242582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
24261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
242782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
242882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
24290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
24304dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
24314dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
24320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
243382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
24344dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
24354dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
24364dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
24374dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
24381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
24391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2440dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2441dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
244282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2443f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
2444dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2445dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2446dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2447f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=");
2448f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(dataId);
24491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2450dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2451dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
24520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
24530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
24540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
24550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2456dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2457dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2458dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2459dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
2460f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2461f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2462f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2463f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2464f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2465f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2466f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
2467f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (")
2468f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2469f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(",")
2470f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2471f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(")" + " AND " + Data.DATA1 + "=");
2472f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2473f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND ((" + DataColumns.MIMETYPE_ID + "=")
2474f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2475f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2476f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol);
2477dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2478f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2479f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2480dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2481f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=")
2482f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2483f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append("))");
2484dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
2485f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=")
2486f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2487f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2488f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol)
2489f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.DATA + "=");
2490f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2491dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2492f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2493f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2494dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2495dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
24961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
249782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
2498f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=")
249982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                        .append(values.getAsLong(StatusUpdates.DATA_ID));
2500dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
250170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2502f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
250370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
25041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
25051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2506de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
2507c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    mSb.toString(), null, null, null,
2508c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
25091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
251067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
25115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2512e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
25131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
25141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
25151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
25161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
25171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
251831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
251931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
252031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
25211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
25221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
252382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2524a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2525a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2526a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2527a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2528a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2529a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2530a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
253182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2532a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2533a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
253482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
253582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
253682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
253782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
253882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2539a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
254082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
254182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
25421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2543a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2544a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2545a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2546e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
25470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
254882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
254982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
25500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
25510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
25520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
25530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
25540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
25550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
25560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
25570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
25580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
25590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
25600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
25610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2562a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
2563a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
2564a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
256582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
256682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
2567a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
2568a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
25690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 3, status);
25700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 4, resPackage);
25710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 5, iconResource);
25720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 6, labelResource);
2573a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
2574a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
2575a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2576a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
2577a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
25780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 2, status);
25790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 3, resPackage);
25800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 4, iconResource);
25810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 5, labelResource);
2582a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
2583a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
2584a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
25850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
2586a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
25870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 2, status);
2588a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
25890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 4, status);
2590a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
25910a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
25920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 1, resPackage);
25930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 2, iconResource);
25940a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 3, labelResource);
25950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
25960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
2597a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
2598e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2599e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2600bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2601a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2602a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
2603a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
2604a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
2605a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2606a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2607a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
26081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
26091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
26104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2611de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2612bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2613b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2614b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2615b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2616f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2617f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2618508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2619508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
262035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2621b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
262235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2623b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2624b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2625b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2626b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2627b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2628b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2629cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2630cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2631cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2632cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2633cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2634d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2635d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2636cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
26376bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
26386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
26392e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
26402e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
26412e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
26422e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
26432e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
26442e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
26452e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
26462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
26472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
26482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
26492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
26502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
26512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
26522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
26532971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2654e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
26552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
26562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
26572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2658f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        numDeletes += deleteRawContact(rawContactId, callerIsSyncAdapter);
26592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
26602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
26612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
26622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
26632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
26642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
26652971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
26665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
26672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2668f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteRawContact(rawContactId, callerIsSyncAdapter);
2669508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2670508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
267120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2672f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2673944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2674f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
267520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
267620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
267748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
267848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
267948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
268048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2681508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2682f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2683f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteData(Data._ID + "=" + dataId, null, callerIsSyncAdapter);
2684ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2685ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2686ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2687f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
26885aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
26892971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
26902971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
26912971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
26922971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
26932971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2694e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
26952971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
26962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
26975aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
26982971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
26992971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
270281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2703f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
270481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
27052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2706508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2707508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2708eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
270943880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
27105aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteSettings(uri, selection, selectionArgs);
2711eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2712eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
271382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
27140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
27151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
27161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
271781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
271881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
27193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
272081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2721508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
27224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
272446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static boolean readBooleanQueryParameter(Uri uri, String name, boolean defaultValue) {
27252971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        final String flag = uri.getQueryParameter(name);
27262971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        return flag == null
27272971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                ? defaultValue
27282971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                : (!"false".equals(flag.toLowerCase()) && !"0".equals(flag.toLowerCase()));
272994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
273094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
27315aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2732b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
273394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2734de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
273594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
273694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
273794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
273894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2739f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2740de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
274194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
274294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
274394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2744f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2745de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
274694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
274794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
27481a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
274994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
275094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
275194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
27525aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2753e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
27541a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2755e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2756e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2757e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2758cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
2759cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2760cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
2761cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2762cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2763cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2764cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
2765cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2766cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2767cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2768cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2769cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2770cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2771cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2772cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2773f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    public int deleteRawContact(long rawContactId, boolean callerIsSyncAdapter) {
2774f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
277514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2776de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
277733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2778b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2779cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
278033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
278133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
278233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
27830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
27840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        // TODO delete from both tables: presence and status_updates
27850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
27860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
27870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2788cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
278981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
279081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2791cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2792cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2793cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2794cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2795cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2796cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2797cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
2798cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2799cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
28004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2801de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2802de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2803bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2804b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2805b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2806b5a4add17815167d20a90645779df34cdf45280dFred Quintana
280735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
280800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
280900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2810b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2811b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
2812b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Object data = values.get(ContactsContract.SyncStateColumns.DATA);
2813b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
2814b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2815b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2816b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2817f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2818f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
281900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
282035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2821b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2822b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2823b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2824b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2825b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2826b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2827b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2828b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2829b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2830b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2831b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
283235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2833d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
28348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
283500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
283600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
283700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2838d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
28398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
2840c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2841c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2842c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
28432e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
28442e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
28452e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
28462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
28472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
28482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
28492e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
28502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
28512e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
28528c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
28532e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
28542e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
28552e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
28567d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
28577d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
28587d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
28597d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
28607d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
28617d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
28627d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
28637d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
28647d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
28657d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
286620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2867944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2868f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
286981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2870f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
287181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
287220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
287320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2874c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
287548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
287648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
287748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
287848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2879f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
288081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2881f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
288281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
288300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
288400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
28857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
28865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
28875ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
28884529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
28897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
28907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
28917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
28925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
289333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
28944529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
28954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId
28964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
28974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
289827f039b535f98c1cb1a31207047003235ddaed15Dmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId, null);
28994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
29007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
29017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
29027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2903ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
29045aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2905f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
290681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2907f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
290881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2909ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2910ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2911ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2912ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2913ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
291473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                String selectionWithId = (Groups._ID + "=" + groupId + " ")
291573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
29165aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
29175aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
291881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2919f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
292081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2921ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2922ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2923ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2924127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2925de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2926b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2927b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2928b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2929eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
29305aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateSettings(uri, values, selection, selectionArgs);
293143880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2932eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2933eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2934eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
293581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
293681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2937f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
293881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
293900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
294000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
294100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
29424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
29434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
29445aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
2945f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
294673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
294773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2948f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
294973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
295073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
295173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
295273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
295373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
295473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
295573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
295673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2957ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
29581a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
29591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
296094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
29616ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
29626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi	        && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
29636ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            final long groupId = ContentUris.parseId(uri);
29646ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
29656ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    Groups.ACCOUNT_TYPE}, Groups._ID + "=" + groupId, null, null,
29666ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
29676ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
29686ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
29696ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
29706ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
29716ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
29726ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
29736ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
29746ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
29756ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi	                ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
29766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
29776ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
29786ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
29796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
29806ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
29816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
29826ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
29836ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
298494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
298594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
298694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2987b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
2988b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
2989e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
29901a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
29911a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2992e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2993e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2994e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2995e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
29964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
29974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
29984529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
29994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
30004529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
300173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
30024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3003b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
300451bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
30054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
30064529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
30074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
30084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
30094529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
30104529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
30114529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
30124529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
30134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
30144529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
30154529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
30164529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
30174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
30184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
30194529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
302019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final String selection = RawContacts._ID + " = " + rawContactId;
302119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
302219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
302319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
302419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
302519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
302619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    null, null, null, null);
302719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
302819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
302919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
303019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
303119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
303219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
303319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
303419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
303519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
303619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
303719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, null);
30385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3039433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
30404529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3041433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3042285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
3043285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateLookupKey(mDb, rawContactId);
3044285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
304519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
304619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
304719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                mInsertedRawContacts.add(rawContactId);
304819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
30495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
30505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
305133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
305233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3053321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3054f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
305520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
305620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
305720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
30585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
305920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
306020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
306120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
306220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
306320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3064b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
306520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
306620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
306770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
306870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
306920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
307020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
307120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
307270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
307320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
307470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
307520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
307670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
307720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
307870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
307920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
308020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3081653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
308220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3083653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3084653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
308514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3086653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3087653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3088f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
308920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3090653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3091653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
309220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
309320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3094653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
309520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
309620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3097f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3098653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3099653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3100321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3101653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
310214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3103a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3104f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
31058e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
3106a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
3107a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
3108a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
31098e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
3110653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return 1;
3111321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3112321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
31138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
31148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
31158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3116b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
31178c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
31188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
31198c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
31208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
31218c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
31228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
31238c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
31248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
31258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
31268c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
31278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
31288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
31308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
31318c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3133d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
31348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3135b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3136d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3137b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3138d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3139b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3140d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3141b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3142d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3143b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3144d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3145d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3146d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
31478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3148d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3149d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3150d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
31518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3152c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
31538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3154c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3155c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
31568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=" + contactId, null);
31578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
31598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
31608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3161b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
31628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3163b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
31648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3165b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
31668c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3167b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
31688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3169b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
31708c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
31718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31728c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=" + contactId, null);
3173f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3174d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3175ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    public void updateContactLastContactedTime(long contactId, long lastTimeContacted) {
3176ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
3177ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(2, contactId);
3178ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.execute();
3179d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
3180d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3181127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3182127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
31830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
31840c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
318580c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
31860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
31870c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
31880c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
31890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
31900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
31910c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
31920c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3193b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3194127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
31950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
31960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
31970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId1 + " AND "
31980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId2, null);
31990c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
32006bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
32016bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
32020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
32030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
32040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
32050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3206127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3207127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3208dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
3209dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
3210dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3211b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
32120c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
32130c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
3214b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
32150c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
3216127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3217127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3218127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3219127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3220b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3221b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
322270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3223b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
322470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        if (mDb == null) return;
322570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
322670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        Set<Account> validAccounts = Sets.newHashSet();
322770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        for (Account account : accounts) {
322870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            validAccounts.add(new Account(account.name, account.type));
322970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
323070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        ArrayList<Account> accountsToDelete = new ArrayList<Account>();
323170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
323270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
323370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
323448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
32355f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana            for (String table : new String[]{Tables.RAW_CONTACTS, Tables.GROUPS, Tables.SETTINGS}) {
32365f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                // Find all the accounts the contacts DB knows about, mark the ones that aren't
32375f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                // in the valid set for deletion.
32385f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Cursor c = mDb.rawQuery("SELECT DISTINCT account_name, account_type from "
32395f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        + table, null);
32405f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                while (c.moveToNext()) {
32415f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                    if (c.getString(0) != null && c.getString(1) != null) {
32425f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        Account currAccount = new Account(c.getString(0), c.getString(1));
32435f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        if (!validAccounts.contains(currAccount)) {
32445f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                            accountsToDelete.add(currAccount);
32455f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        }
324670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                    }
324770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                }
32485f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                c.close();
324970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
325070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
325170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
32525f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
325370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                String[] params = new String[]{account.name, account.type};
325470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.GROUPS
325570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?", params);
325670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.PRESENCE
325770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (SELECT "
325870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + RawContacts._ID + " FROM " + Tables.RAW_CONTACTS
325970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?)", params);
326070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.RAW_CONTACTS
326170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?", params);
32625f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                mDb.execSQL("DELETE FROM " + Tables.SETTINGS
32635f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        + " WHERE account_name = ? AND account_type = ?", params);
326470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
3265b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
326670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
326770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
326870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
326970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
327070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3271619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3272619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3273622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
3274622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
327567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
327667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
327767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
327867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
327967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
328067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
328167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
328267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
328367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
328467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
328567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
328667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
3287dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
3288622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
3289dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
3290dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
3291622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
3292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
3293dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
3294622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
3295622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
32964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
32974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
32984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
3299bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3300bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3301bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
33020b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3303b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
330435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3305d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
33061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3307c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3308c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3309619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3310619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3311a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
33124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
331335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3314b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
331535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
331635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3317d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3318763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3319619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3320619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3321619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3322d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
33234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3324763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
33254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + contactId);
33266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
33276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
33286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
33295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
33305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
33315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
33325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
33335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
33345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
33355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
33365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
33375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
33385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
33395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3340763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
33415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=" + contactId + " AND " +
33425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            Contacts.LOOKUP_KEY + "=");
33435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhereEscapeString(lookupKey);
33445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, selectionArgs, sortOrder,
33455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
33465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
33475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
33485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
33495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
33505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
33515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
33525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3353763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
33545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
33555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
33565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
33575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3358f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3359f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
3360f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
3361763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3362f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
3363f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
3364f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3365f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3366f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
3367ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3368763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3369ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
33704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
33714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3372e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
33735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
33744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sb.toString());
3375ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3376ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3377ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3378ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3379ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3380ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
33814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3382ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3383d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
33844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
33854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3386e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
33875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
33884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
33894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
33904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3391763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3392ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
33935e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
33945e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
33955e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
33965e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
33975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
33985e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
33995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
34004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
34014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3403d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
34055e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
34064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3407d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3408d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3409d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3410763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
34114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3413d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34145e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
34155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3416d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3417d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
34184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3419d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3420d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3421d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3422d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
34234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
34244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3425d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3426d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3427d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3428d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3429d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3430d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3431ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3432763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3433b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
343471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
34354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3436b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3437b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3438b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3439b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3440d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
34414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
344282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
34434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
34446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
34456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
344600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3447ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
34483653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
344982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
34503653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
34513653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
34523653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
34533653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
34543653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
34554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
345682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
345789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
34582815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
34592815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
34602815f58f72f109790585931f601a63ddc02536a5Evan Millar
346148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
346282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
346348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
346448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
346548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
346648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
346748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3468ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
346982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
347089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3471ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
34724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
34734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
34745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
34755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
34765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
34775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
34785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
34795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
348020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
34815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
34825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
34835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
34845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
34855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
34865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
34875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
34885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
34895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
34905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
34915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
34925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
34935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
34945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
34955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
34965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
34975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
34984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
3499ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
35005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3501a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3502a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3503a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3504ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3505ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3506ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
35074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
350882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
350989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
35114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
35124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
351348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
351482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
351548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
351648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
351748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
351848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
351948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
35205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
352182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
352289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
35245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + Email.DATA + "=");
35254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhereEscapeString(uri.getLastPathSegment());
35264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3527ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3528ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3529ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
35305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
353182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
353289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
35345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
35355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
35365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
35375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
353820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
353920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
354020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
354120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            sb.append(Data.RAW_CONTACT_ID + " IN ");
354220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
354320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            sb.append(" OR ");
354420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
35455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
35465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
35475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(Email.DATA + " LIKE ");
35481e530df9f7e496dc47f77d4323c89bd413b79b64Dmitri Plotnikov                    sb.append(DatabaseUtils.sqlEscapeString(filterParam + '%'));
35495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
35505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
35515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
35525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3553a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3554a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3555a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
35565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
35575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
35585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3559ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
356082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
356189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
356289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3563ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3564ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3565ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
356648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
356782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
356848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
356948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
357048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
357148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
357248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
357348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
35745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
3575763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
35764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
35774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
35784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
35805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
3581763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
358289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
35834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
35844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
35854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
35875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
358882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
358989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=" + rawContactId);
3590e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3591e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3592e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3593e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
359482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3595e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3596e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3597e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
35984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
359982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
360082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + ContentUris.parseId(uri));
3601a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3602a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3603a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3604a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
36054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3606a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3607a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3608a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3609e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
3610a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3611a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3612e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3613b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
3614e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3615e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
3616e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3617e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3618e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3619a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3620a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3622ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
3623b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3624ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
362589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3626ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3627ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3628ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3629ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3630ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
3631b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3632ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
363389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(Groups._ID + "=" + groupId);
3634ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3635ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3636ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3637ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
3638b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3639ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
364089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
364189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3642ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3643ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3644ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3645b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
36460c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3647b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3648b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3649b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3650b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
365131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3652d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
36532d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
36542d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
36552d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
36562d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
365731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3658d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3659d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
366031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
366131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
366231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
366331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3664763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
36657581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
36667581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
36672d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
366831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
366931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3670eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3671eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3672eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
367389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3674e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3675e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3676e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3677b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3678e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
367982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3680b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3681e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3682e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
368382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3684b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3685e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3686e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3687e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3688eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3689eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3690eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
369182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
36920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
36935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
36945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
36955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
369682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
36970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
36980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri));
36995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
37005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
37015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3702c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3703a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3704c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3705c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3706c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
3707b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3708b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
3709c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3710c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
37111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
3712b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
37141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
37161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
3717b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
37191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
37201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
37221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
3723b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
37251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
37261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
37281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
3729b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
373171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
37321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
373546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
373646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
373746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
373846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
373946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
374046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
374146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
374246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
374346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
374446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
374546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
374646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
37474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
3748f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3749c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
37504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
37514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
37535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
37545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
37565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
37575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
3758038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
3759038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
3760038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
3761038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
37625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
37635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
37644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
37654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
37664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
37674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
37684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
37694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
37715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
37725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
37735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
37755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (contactId == -1) {
37765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
37775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
37785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
37805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
37815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
37835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
37845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
37865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
37875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
37885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
37895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
37905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
37915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
37935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
37945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
37955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
37965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
37975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
37985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
37995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
38005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int sourceIdCount = 0;
38015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
38025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
38035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
38045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sourceIdCount++;
38055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
38065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (sourceIdCount == 0) {
38095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
38105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
38135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
38145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
38155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
38165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
38175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
38185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
38195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
38205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
38215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
38235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
38245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
38265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
38275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
38285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
38295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
38305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
38315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
38325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
38335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
38345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
38355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
38365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
38375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
38385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
38395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
38405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
38415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
38425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
38435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
38445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
38455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
38485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
38495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupByDisplayNameQuery {
38515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
38525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
38545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
38555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
38565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
38575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
38585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
38595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
38615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
38625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
38635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int NORMALIZED_NAME = 3;
38645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
38655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
38675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
38685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int displayNameCount = 0;
38695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
38705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
38715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
38725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                displayNameCount++;
38735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
38745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
38775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
38785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
38815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
38825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
38835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
38845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
38855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
38865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
38875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
38885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
38895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
38915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
38925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
38935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
38955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
38965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
38975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
38985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
38995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
39005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
39015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
39025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
39035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
39045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
39055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
39065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
39075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
39085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
39095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
39105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
39115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
39125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
39135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
39145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
39175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
39185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
39205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
39215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
39225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
39235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
39245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
39265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
39275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
39295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
39305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
39325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
39335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
39345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
39355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
39365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
39375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
39385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
39395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
39405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
39415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
39425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
39435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
39445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
39455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
39465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
39485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
39495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
39505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
39515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
39535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3954763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
3955763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
395682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
3957763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
3958763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        String requestingPackage = uri.getQueryParameter(
3959763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
3960763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
3961d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
3962763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
3963763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
3964b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
396582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
396682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
396782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
396882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
3969b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
397082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
397182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
397282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
397382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
397482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
39753296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
39763296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
3977a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
39783296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
397982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
398082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
398182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
398282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
3983ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
3984763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
3985763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
3986763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
3987763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        String requestingPackage = uri.getQueryParameter(
3988763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
3989763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
3990d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
3991763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
3992763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
3993763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
3994763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
3995763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
3996763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
3997763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
399846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
399946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        // Note: currently, "export only" equals to "restricted", but may not in the future.
400046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
400146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.FOR_EXPORT_ONLY, false);
400246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
400346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        String requestingPackage = uri.getQueryParameter(
400446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
400546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        if (requestingPackage != null) {
400646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            excludeRestrictedData = excludeRestrictedData
400746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
400846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        }
400946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
401046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setProjectionMap(sRawContactsEntityProjectionMap);
401146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
401246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
401346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
401482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
401582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
401682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4017d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa        // Note: currently, "export only" equals to "restricted", but may not in the future.
4018763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
4019d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa                Data.FOR_EXPORT_ONLY, false);
4020763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4021763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        String requestingPackage = uri.getQueryParameter(
4022763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4023763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4024763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            excludeRestrictedData = excludeRestrictedData
4025d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4026763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4027763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4028763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getDataView(excludeRestrictedData));
402982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
403082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
40313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated presence when requested
4032b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
403382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
40343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
403582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
403682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
403782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
40383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated status updates when requested
4039b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
404082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS,
404182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
404282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
404382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
404482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
40453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
40463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
404782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
40483296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
4049ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
40503296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
40513296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual presence when requested
40523296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
40533296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
40543296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdates.DATA_ID + "="
40553296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + DataColumns.CONCRETE_ID + ")");
40563296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
40573296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
40583296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual status updates when requested
40593296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection,
40603296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS,
40613296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_RES_PACKAGE,
40623296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_ICON,
40633296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_LABEL,
40643296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_TIMESTAMP)) {
40653296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
40663296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
40673296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + DataColumns.CONCRETE_ID + ")");
40683296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
40693296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
407082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
407182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
407282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4073ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4074ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
40750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
40760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
40770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4078b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
40790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
40800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4081b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
40820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
40830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
40840a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
40850a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
40860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4087b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
40880a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
40890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
40900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
40910a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
40920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
40930a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
40940a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
40950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
40960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
40970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setTables(sb.toString());
40980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
40990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
41000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
41014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
41024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
41034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
41044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
41054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
41064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
41074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
41084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
41094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
41104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
41114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
41124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
41134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4114e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
4115e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
4116e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
4117e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        if (!TextUtils.isEmpty(accountName)) {
4118e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
4119e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4120e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
4121e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
4122e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
4123e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
4124e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
4125e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
4126e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
4127e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
4128e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
4129e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
4130e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
4131e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
4132e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
41337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
4134c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4135c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
4136c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4137c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4138c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
4139c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private String getLimit(Uri url) {
4140c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limitParam = url.getQueryParameter("limit");
4141c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
4142c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4143c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4144c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
4145c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
4146c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
4147c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
4148c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
4149c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
4150c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4151c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
4152c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
4153c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
4154c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4155c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4156c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4157c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
41585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
41595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
41605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
41615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
41625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
41635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
41645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
41655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
41665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
41675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
41685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
41695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
41705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
41715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
41725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
41735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
41745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
41755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
41765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
41775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
41785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
41795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
41805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
41815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
41825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
41835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
41845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
41855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
41865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
41875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
418800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
4189d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
419070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
419170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
41926cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
419370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
419470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
419570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
419670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
4197d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
419870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
419967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
42005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
42015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4202619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
4203619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
4204619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4205b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
4206b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
4207b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
4208b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
4209d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
4210b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                if (!"r".equals(mode)) {
4211b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
4212b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
4213b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4214b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4215b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4216b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                String sql =
4217b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
4218b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
4219b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=" + contactId;
4220b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
4221b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql, null);
4222d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4223d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4224f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4225d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
4226d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
422714b8a1243ab5c043b35e47527ca1c962064f3771Daisuke Miyakawa                final String selection = Contacts._ID + "=" + contactId;
4228d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4229d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
4230d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
4231d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
4232d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
4233d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                outputRawContactsAsVCard(localStream, selection, null);
4234d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
4235d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4236b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4237b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
4238b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new FileNotFoundException("No file at: " + uri);
4239b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
4240b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
4241b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4242d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
4243d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String VCARD_TYPE_DEFAULT = "default";
4244d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4245d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4246d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
4247d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
4248d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4249d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
4250d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
4251d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
4252d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
4253d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4254d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
4255d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
4256d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4257d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
4258d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
4259d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
4260b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4261d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
4262d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
4263d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
4264d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4265d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
4266d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4267d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4268d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4269d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
4270d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
4271d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
4272d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4273d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
4274d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
4275d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
4276d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
4277d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
4278d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4279f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
4280d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        if (!composer.init(selection, selectionArgs))
4281d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
4282d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4283d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
4284d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
4285d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
4286d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4287d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4288d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
4289d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4290b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4291bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
4292bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov    private static Account readAccountFromQueryParams(Uri uri) {
4293bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        final String name = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
4294bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        final String type = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
4295bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
4296bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov            return null;
4297bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        }
4298bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        return new Account(name, type);
4299bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov    }
4300bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
4301bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
4302619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
43037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
43047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
43057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
4306d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static class RawContactsEntityIterator implements EntityIterator {
43077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
43087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
43097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
43107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
43117a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
43127a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
43137a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
43147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
43157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
43167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
43177a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
43187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
43197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
43207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
43217a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
43227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
43237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
43247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
43257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
43267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
43277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
43287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
43297a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4};
43307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
433146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public static final String[] PROJECTION = new String[]{
43326cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
43336cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
43346cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.SOURCE_ID,
43356cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.VERSION,
43366cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.DIRTY,
433746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts.Entity.DATA_ID,
43387a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RES_PACKAGE,
43397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.MIMETYPE,
43407a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
43417a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
43427a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
43437a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
43447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
43457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
43467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
43477a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
43487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
43497a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
43507a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
43517a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
43527a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
43537a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
43547a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
43557a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
43567a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
43577a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
43587a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4,
435946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts._ID,
43607a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.IS_PRIMARY,
43613cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar                Data.IS_SUPER_PRIMARY,
43627a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA_VERSION,
43637a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                GroupMembership.GROUP_SOURCE_ID,
43647a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC1,
43657a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC2,
43667a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC3,
436794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                RawContacts.SYNC4,
436838446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey                RawContacts.DELETED,
4369c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.CONTACT_ID,
4370c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.STARRED};
4371035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
4372035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
4373035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
4374035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
4375035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
4376035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
4377035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
437867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
437967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
438067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
43817a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_RAW_CONTACT_ID = 27;
43827a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_IS_PRIMARY = 28;
43833cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_IS_SUPER_PRIMARY = 29;
43843cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DATA_VERSION = 30;
43853cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_GROUP_SOURCE_ID = 31;
43863cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC1 = 32;
43873cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC2 = 33;
43883cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC3 = 34;
43893cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC4 = 35;
43903cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DELETED = 36;
43913cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_CONTACT_ID = 37;
43923cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_STARRED = 38;
43937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
439446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public RawContactsEntityIterator(ContactsProvider2 provider, Uri entityUri,
439546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String contactsIdString,
439646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String selection, String[] selectionArgs, String sortOrder) {
43977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
439846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            Uri uri;
43997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
440046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, contactsIdString);
440146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(uri, RawContacts.Entity.CONTENT_DIRECTORY);
440246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            } else {
440346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = ContactsContract.RawContactsEntity.CONTENT_URI;
4404035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
440546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            final Uri.Builder builder = uri.buildUpon();
440646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            String query = entityUri.getQuery();
440746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            builder.encodedQuery(query);
440846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            mEntityCursor = provider.query(builder.build(),
440946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    PROJECTION, selection, selectionArgs, sortOrder);
44107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
44117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
44127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4413038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4414038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4415038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4416038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4417038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4418038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4419038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana
44207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
44217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
44227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
44237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
44247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
44257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
44267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
44277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
44297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
44307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
44317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
44327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
44347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
44357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
44377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
44387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
44397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
44407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
44417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
44427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
44437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
44457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
44477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
44497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
44506cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
44516cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
44525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
44536cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
44546cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
44556cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
44567a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
44577a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
44587a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
44597a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
446094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            contactValues.put(RawContacts.DELETED, c.getLong(COLUMN_DELETED));
446138446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey            contactValues.put(RawContacts.CONTACT_ID, c.getLong(COLUMN_CONTACT_ID));
4462c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            contactValues.put(RawContacts.STARRED, c.getLong(COLUMN_STARRED));
44637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
44647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
44667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
44677a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
44687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
44697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
447023c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                if (c.isNull(COLUMN_CONTACT_ID)) {
447123c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                    continue;
447223c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                }
44737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
44747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
447523c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data._ID, c.getLong(COLUMN_DATA_ID));
44767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
44777a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
447823c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_PRIMARY, c.getLong(COLUMN_IS_PRIMARY));
447923c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_SUPER_PRIMARY, c.getLong(COLUMN_IS_SUPER_PRIMARY));
44807a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
44819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
44829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
44839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
44849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
44857a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
44867a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                for (int i = 0; i < DATA_KEYS.length; i++) {
44877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
44887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
44897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
44907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
44917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
44927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
44937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
44947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
44957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
44967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
44977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
44987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
44997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
45007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
45017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
45027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
45037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
45057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
45067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
45077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4508226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    /**
4509226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
4510226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
4511226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     */
4512226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    private static class GroupsEntityIterator implements EntityIterator {
4513226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private final Cursor mEntityCursor;
4514226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private volatile boolean mIsClosed;
4515226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4516226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final String[] PROJECTION = new String[]{
4517226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups._ID,
4518226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_NAME,
4519226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_TYPE,
4520226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.SOURCE_ID,
4521226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.DIRTY,
4522226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.VERSION,
4523226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.RES_PACKAGE,
4524226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE,
4525226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE_RES,
45267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.GROUP_VISIBLE,
45277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC1,
45287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC2,
45297a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC3,
45307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC4,
45317a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYSTEM_ID,
453294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.NOTES,
45331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.DELETED,
45341a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.SHOULD_SYNC};
4535226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4536226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ID = 0;
4537226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_NAME = 1;
4538226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 2;
4539226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_SOURCE_ID = 3;
4540226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_DIRTY = 4;
4541226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_VERSION = 5;
4542226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_RES_PACKAGE = 6;
4543226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE = 7;
4544226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE_RES = 8;
4545226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_GROUP_VISIBLE = 9;
45467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 10;
45477a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 11;
45487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 12;
45497a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 13;
45507a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYSTEM_ID = 14;
45517a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_NOTES = 15;
455294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        private static final int COLUMN_DELETED = 16;
45531a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        private static final int COLUMN_SHOULD_SYNC = 17;
4554226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4555226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
4556226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
4557226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = false;
4558226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4559226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String updatedSortOrder = (sortOrder == null)
4560226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    ? Groups._ID
4561226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    : (Groups._ID + "," + sortOrder);
4562226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4563b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            final SQLiteDatabase db = provider.mDbHelper.getReadableDatabase();
4564226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4565b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            qb.setTables(provider.mDbHelper.getGroupView());
4566226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setProjectionMap(sGroupsProjectionMap);
4567226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (groupIdString != null) {
4568226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups._ID + "=" + groupIdString);
4569226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4570226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountName = uri.getQueryParameter(Groups.ACCOUNT_NAME);
4571226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountType = uri.getQueryParameter(Groups.ACCOUNT_TYPE);
4572226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!TextUtils.isEmpty(accountName)) {
4573226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups.ACCOUNT_NAME + "="
4574226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4575226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + Groups.ACCOUNT_TYPE + "="
4576226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
4577226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4578226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
4579226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    null, null, updatedSortOrder);
4580226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToFirst();
4581226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4582226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4583226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public void close() {
4584226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4585226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("closing when already closed");
4586226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4587226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = true;
4588226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.close();
4589226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4590226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4591226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public boolean hasNext() throws RemoteException {
4592226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4593226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
4594226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4595226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4596226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return !mEntityCursor.isAfterLast();
4597226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4598226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4599038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4600038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4601038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4602038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4603038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4604038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4605e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4606226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public Entity next() throws RemoteException {
4607226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4608226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
4609226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4610226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!hasNext()) {
4611226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
4612226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4613226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4614226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
4615226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4616226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final long groupId = c.getLong(COLUMN_ID);
4617226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4618226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            // we expect the cursor is already at the row we need to read from
4619226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            ContentValues groupValues = new ContentValues();
4620226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4621226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4622226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups._ID, groupId);
4623226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
4624226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
4625226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4626226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4627226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
4628226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
4629226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
46307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
46317a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
46327a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
46337a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
46347a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
463594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            groupValues.put(Groups.DELETED, c.getLong(COLUMN_DELETED));
46367a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
46371a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            groupValues.put(Groups.SHOULD_SYNC, c.getString(COLUMN_SHOULD_SYNC));
4638226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            Entity group = new Entity(groupValues);
4639226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4640226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToNext();
4641226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4642226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return group;
4643226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4644226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    }
4645226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4646a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
46477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
46487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
4649568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
4650568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
46517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
46527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
46535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS:
46545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID:
46557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
46565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (match == RAW_CONTACTS_ID) {
46577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
46587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
46597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
466046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                return new RawContactsEntityIterator(this, uri, contactsIdString,
466146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                        selection, selectionArgs, sortOrder);
4662226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS:
4663226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS_ID:
4664226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String idString = null;
4665226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                if (match == GROUPS_ID) {
4666226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    idString = uri.getPathSegments().get(1);
4667226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                }
4668226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4669226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                return new GroupsEntityIterator(this, idString,
4670226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        uri, selection, selectionArgs, sortOrder);
46717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
46727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
46737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
46747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
46757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
46764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
46774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4678a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
46794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4680b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4681b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP:
4682be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
4683b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4684b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4685b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4686f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
4687f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4688b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4689be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4690b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4691b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4692508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4693b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
469448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
469548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
469648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
469748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
469848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
469948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
470048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
470148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
470248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
470348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
470448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
470548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4706b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4707b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4708b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4709b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4710b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4711b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4712b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4713b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4714c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4715c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4716c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4717c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
471861efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
471961efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
47204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
47214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
47227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
472325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
47243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
472525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
47263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
472725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindNull(1);
47283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
472925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
473025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
473125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
47323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
47333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
473473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
473573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
473673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
473773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
473873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.bindLong(1, rawContactId);
473973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.execute();
474073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
474173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4742c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4743c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
4744c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
4745c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4746c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4747c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4748653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
4749c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
4750653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
4751653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
4752c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
4753c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4754c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
4755c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4756c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
4757c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
4758c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4759c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4760c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4761653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
4762c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
4763653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
4764653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
4765c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
4766c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4767ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4768f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
4769f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
4770f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4771f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4772f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4773f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
4774f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokens.length == 0) {
4775f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4776f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4777f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4778f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String address = tokens[0].getAddress();
4779f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int at = address.indexOf('@');
4780f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (at != -1) {
4781f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            address = address.substring(0, at);
4782f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4783f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4784f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4785f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
4786f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4787f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4788f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4789f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
4790f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4791f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
4792f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
4793f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4794f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4795f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4796f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4797f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
4798f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4799f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4800a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
4801a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
4802a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
4803a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4804a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
4805a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4806a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
4807a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4808a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
4809a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4810a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
4811f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4812f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
4813f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
4814f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4815f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4816f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4817f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
4818f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4819f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
4820f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SoftReference<String[]> ref;
4821f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4822f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        synchronized (mNicknameClusterCache) {
4823f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
4824f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
4825f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                if (ref == null) {
4826f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    return null;
4827f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4828f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = ref.get();
4829f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4830f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4831f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4832f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (clusters == null) {
4833f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
4834f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
4835f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
4836f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
4837f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4838f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4839f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4840f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4841f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4842f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
4843b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
4844f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4845f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
4846f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
4847f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                null, null, null);
4848f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
4849f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
4850f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (count > 0) {
4851f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = new String[count];
4852f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int i = 0; i < count; i++) {
4853f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    cursor.moveToNext();
4854f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
4855f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4856f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4857f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        } finally {
4858f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            cursor.close();
4859f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4860f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4861f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4862f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4863f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
4864f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4865f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
4866f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
4867f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4868f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4869f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4870f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
4871f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
4872f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
4873f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4874f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4875f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4876f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
4877f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
4878f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4879f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4880f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4881f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4882f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
4883f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4884f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
4885f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
4886f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
4887f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
4888f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
4889f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
4890f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4891f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4892f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4893f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
4894f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4895f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
4896f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
4897f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
4898f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4899f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
49002d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
4901d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4902d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
4903d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
4904d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
4905d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
4906d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
4907d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
4908e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
4909d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN("
4910d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
4911d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
4912d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NICKNAME + ","
4913d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.ORGANIZATION + "))");
4914e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
4915e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
49165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
4917c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4918c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
4919c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
4920c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4921c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4922a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
4923c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            String limit) {
492420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), limit,
492520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                true);
49265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
49275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
49285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
492920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            String limit, boolean allowEmailMatch) {
4930d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4931d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + NameLookupColumns.RAW_CONTACT_ID +
4932d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
4933d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
4934d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
49355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
4936a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
4937a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
4938a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
493920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                + NameLookupType.ORGANIZATION);
494020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
494120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
494220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
494320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        sb.append(")");
49443de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
4945c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limit != null) {
4946c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
4947c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4948c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(")");
4949ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
4950ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
49514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
49524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
49534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
49544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
4955b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
4956b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
4957b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
4958b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
4959b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
49604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
49614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
4962b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
4963b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
4964b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
4965caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
49665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
49675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
49685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
49695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
49705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
49715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
49725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
49735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
49745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
49755e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
49765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
4977caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
4978caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
4979caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
4980df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
4981df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
4982caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
4983caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
4984caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
4985caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
49866f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
4987caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
49886f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
4989caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
49904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
4991