ContactsProvider2.java revision ea029fd79225640e49be82457b83b6b3a0279fd0
14f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/*
24f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Copyright (C) 2009 The Android Open Source Project
34f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
44f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License");
54f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * you may not use this file except in compliance with the License.
64f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * You may obtain a copy of the License at
74f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
84f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *      http://www.apache.org/licenses/LICENSE-2.0
94f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Unless required by applicable law or agreed to in writing, software
114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS,
124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * See the License for the specific language governing permissions and
144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * limitations under the License
154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
1828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar
193de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.internal.content.SyncStateContentProviderHelper;
203de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
21b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
22b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
23b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
24b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkeyimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
26b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
27b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DisplayNameSources;
28b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
29b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
30b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
31b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
32b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NicknameLookupColumns;
33b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
34b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
35b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
36b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
37b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
38b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
39b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
40a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Lists;
41a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Maps;
42a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Sets;
433de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
44b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
45caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
465b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
47c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
48568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
49568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
506ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
5435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
5567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.EntityIterator;
56627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
57568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
59627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
613de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.content.SharedPreferences.Editor;
62b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
64ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
65a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
66b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
67b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
70c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
726ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
73d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
74b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
750e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
76d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.pim.vcard.VCardComposer;
773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
78508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
793de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
803de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.LiveFolders;
813de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.OpenableColumns;
823de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.SyncStateContract;
83b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
843de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
863de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
873de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
883de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
893de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
9082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
92ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
93ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
97de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
98b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
994097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
10067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
101a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
102a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
103f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Token;
104f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Tokenizer;
105c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
107d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
108b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
109d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
110d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
111f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport java.lang.ref.SoftReference;
1127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
114b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1150e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
117622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
118b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1190e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
120ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1265b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
127caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
128bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
129bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
130bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
132619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
133619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
134619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
1393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * Shared preference key for the legacy contact import version. The need for a version
1403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
1433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final String PREF_CONTACTS_IMPORTED = "contacts_imported_v1";
1443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int PREF_CONTACTS_IMPORT_VERSION = 1;
1453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1460e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1470e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
148a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1505e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final String TIMES_CONTACED_SORT_COLUMN = "times_contacted_sort";
1515e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
152d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
1535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            + TIMES_CONTACED_SORT_COLUMN + " DESC, "
1549b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
155d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
156d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
157d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
158d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
159d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
160d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
169f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
1704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
17446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
1754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1766bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
178ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
17948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
18048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
18148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
18248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
18348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
18448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
18548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
18648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
187a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
190b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
191b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
192b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
19382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
19482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
1951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
19631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
19731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
198eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
199eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
200ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
201ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
202ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
203ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
20435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
205b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
20635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
207c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
208c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
209c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2111b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
21546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
21646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
21767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface ContactsQuery {
2185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final String TABLE = Tables.RAW_CONTACTS;
2199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
22067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2216cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2226cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
2236cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
224ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
225ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
22767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_NAME = 1;
22867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_TYPE = 2;
22967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
23067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
231d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
232f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
233f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
234f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
23567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
23667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2376cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
239f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
240ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
241ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
242d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
24367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
244d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
245ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DisplayNameQuery {
24867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
253f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
254a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            Organization.TITLE,
2553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 0;
2583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 1;
259a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        public static final int DATA = 2;
260a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        public static final int TITLE = 3;
2613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
26467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
271f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
27288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
27388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
27488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
27588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
27688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
27788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
27888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
279f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
286f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
290321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
29120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
29220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
293321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
294321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
29520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
29620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
297f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
298f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private interface NicknameLookupQuery {
299f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
300f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
301f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] COLUMNS = new String[] {
302f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
303f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        };
304f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
305f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int CLUSTER = 0;
306f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
307f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
30819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
30919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
31019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
31119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
31219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                ContactsContract.RawContacts.DELETED
31319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
31419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
31519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
31619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
31719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
31825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private static final HashMap<String, Integer> sDisplayNameSources;
3193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    static {
32025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources = new HashMap<String, Integer>();
32125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(StructuredName.CONTENT_ITEM_TYPE,
32225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.STRUCTURED_NAME);
323a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sDisplayNameSources.put(Nickname.CONTENT_ITEM_TYPE,
324a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                DisplayNameSources.NICKNAME);
32525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Organization.CONTENT_ITEM_TYPE,
32625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.ORGANIZATION);
32725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Phone.CONTENT_ITEM_TYPE,
32825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.PHONE);
32925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Email.CONTENT_ITEM_TYPE,
33025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.EMAIL);
3313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
33231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
333c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
334df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
335caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
33671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
33771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
33871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
33971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
34071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
34171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
34271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
34371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
34471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
34571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
34671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
34771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
34871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
34971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
350038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
351038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
352e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
3545e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
3555e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentStarredProjectionMap;
3565e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentFrequentProjectionMap;
357f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
358f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final HashMap<String, String> sContactsVCardProjectionMap;
359ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
360d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
36146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    /** Contains the columns from the raw contacts entity view*/
36246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final HashMap<String, String> sRawContactsEntityProjectionMap;
3634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
368e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
370ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
373373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
374b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
375eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
376eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
37782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
37882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final HashMap<String, String> sStatusUpdatesProjectionMap;
3791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3801b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
382c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
383c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
385c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
386ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    /** Precompiled sql statement for incrementing times contacted for a contact */
387ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    private SQLiteStatement mContactsLastTimeContactedUpdate;
3883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
38925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
39073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /** Precompiled sql statement for marking a raw contact as dirty */
39173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private SQLiteStatement mRawContactDirtyUpdate;
39282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
393a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
394f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
395f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
396a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
397a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
398a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
3990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
400a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
401a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
402f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
403f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
404f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
405f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
4064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
4074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
408a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
409d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
410d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
411d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
4123653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
4133653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4142d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
4152d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4163653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
4175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
4185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
4195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
420f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
4215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
422ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
423ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
4245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
4253653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
4275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
4285ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
42946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
43046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
43146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
432b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
4334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
4344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
435ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
43648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
4375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
438ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
4394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
44048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
4415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
4425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
4434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
444ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
44548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
4461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
447ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
448ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
45135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
452b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
453b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
45435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
455a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
456b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
457b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
458b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
459b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
461eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
462eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
46382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
46482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
4651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
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_QUERY + "/*",
469c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
470c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
471c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
472c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
4741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
4751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
4761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
4771b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
4781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
4791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
4801b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
48119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
48219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
48319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
484038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
485038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
486e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
4884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
4894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
4904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
4914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
4924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
4934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
4944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
4954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
496f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
4974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
4985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
499f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
5003296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
5013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_PRESENCE,
5023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
5033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS,
5043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
5053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
5063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
5073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
5083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
5093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_LABEL,
5103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
5113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_ICON,
5123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
5133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
5145e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5165e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Long.MAX_VALUE + " AS " + TIMES_CONTACED_SORT_COLUMN);
5175e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5205e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Contacts.TIMES_CONTACTED + " AS " + TIMES_CONTACED_SORT_COLUMN);
5215e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
522f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap = Maps.newHashMap();
523f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
524d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
525f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.SIZE, "0 AS " + OpenableColumns.SIZE);
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
5284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
5294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
5354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
5364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
5374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
5384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
5394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
5404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
5414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
5424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
5434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
5444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
5454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
5464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
5472815f58f72f109790585931f601a63ddc02536a5Evan Millar
5484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
5494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
5504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
5514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
5524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
5534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
5544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
5554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
5564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
5574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
5584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
5594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
5604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
5614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
5624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
5634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
5644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
5654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
5664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
5674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
5684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
5694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
5704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
5714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
57582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sDataProjectionMap.put(Data.CONTACT_ID, Data.CONTACT_ID);
5764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
58156d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
589a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
5904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
591a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
59246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        HashMap<String, String> columns;
59346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns = new HashMap<String, String>();
59446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts._ID, RawContacts._ID);
59546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
59646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
59746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
59846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
59946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.VERSION, RawContacts.VERSION);
60046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DIRTY, RawContacts.DIRTY);
60146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DELETED, RawContacts.DELETED);
602bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey        columns.put(RawContacts.IS_RESTRICTED, RawContacts.IS_RESTRICTED);
60346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
60446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
60546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
60646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
60746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
60846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.MIMETYPE, Data.MIMETYPE);
60946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA1, Data.DATA1);
61046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA2, Data.DATA2);
61146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA3, Data.DATA3);
61246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA4, Data.DATA4);
61346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA5, Data.DATA5);
61446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA6, Data.DATA6);
61546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA7, Data.DATA7);
61646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA8, Data.DATA8);
61746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA9, Data.DATA9);
61846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA10, Data.DATA10);
61946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA11, Data.DATA11);
62046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA12, Data.DATA12);
62146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA13, Data.DATA13);
62246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA14, Data.DATA14);
62346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA15, Data.DATA15);
62446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC1, Data.SYNC1);
62546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC2, Data.SYNC2);
62646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC3, Data.SYNC3);
62746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC4, Data.SYNC4);
62846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
62946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.STARRED, Data.STARRED);
63046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
63146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
63246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
63346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
63446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        sRawContactsEntityProjectionMap = columns;
63546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
6363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
6373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
6383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
6393296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
6403296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
6413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
6423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
6443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
6463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6473296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
6483296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
6493296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6503296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
6513296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.PRESENCE,
6523296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
6533296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS,
6543296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
6553296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
6563296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
6573296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
6583296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
6593296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
6603296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
6613296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_ICON,
6623296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
6633296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
6645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
6655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
6665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
6675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
6685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
6695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
6705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
6715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
6735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
6745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
6755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
6765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
6775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
6785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
6795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
6805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
6815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
6825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
6835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
6845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
6855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
6865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
6875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
6885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
6895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
6905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
6915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
6925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
6938f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
6945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
6965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
6975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
6985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
6995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
7005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
701a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
7025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
7035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
7045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
7053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
7063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
7073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
7083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
7093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
7103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
7113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
7133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
7153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
7173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
7183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
7203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
7213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
7223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS,
7233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
7243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
7253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
7273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
7293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
7313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
7323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
733e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
734e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
735e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
73656d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
73756d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar                Contacts.LOOKUP_KEY + " AS " + PhoneLookup.LOOKUP_KEY);
738e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
739e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
740e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
741e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
742e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
743e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
744e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
745e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
746e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
747e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
748e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
749e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
750e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
751e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
752e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
753e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
754e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
755e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
756e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
757e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
758e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
759e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
760e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
761e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
762e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
763e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
7649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
765ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
76789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
768035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
769035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
7709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
7719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
7729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
77389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
774ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
77567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
776ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
7773cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
77894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
7793cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
78038446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
78189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
78289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
78389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
78489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
785ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
7876cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
788ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
789ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
790d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
791d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
792ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
793ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
794ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
795d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
796d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
797ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
798f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
799ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
800ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
801b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
802b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
803b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
804b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
8050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
8060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
807b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
808b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
809eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
810eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
811eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
812eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
813eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
814eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
815341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
816341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
817341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
818fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
819341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
820341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
821341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
82268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
82368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
82468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
82568936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
82668936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
827e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
82868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
82968936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
83068936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
831eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
832eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
833373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
8344dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
8350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
8360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
83782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
83882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
83982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
84070c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
84170c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
84282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
84382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
84482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
84582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
8460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
8470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
8480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
8490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
8500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
85182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sStatusUpdatesProjectionMap = columns;
85219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
8531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
8541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
8551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
8561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
8571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
8581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
8591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
8601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
8611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
8621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
8631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
8644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
8654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
8663296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
8673296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        map.put(toField, fromField + " AS " + toField);
8683296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    }
8693296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
8703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
8713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
8723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
8733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
8743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
876653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
8773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
8793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
880a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
881a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
882a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
883a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
884a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
885a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
886a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
887a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
8883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
890653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
891653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
892b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
893653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
894653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
895653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
896653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
8973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
8983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
8993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
9005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
901e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
902e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
903e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
904e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
905653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
906e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
907e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
908e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
9093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
9133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
9143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
915653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
916f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
91714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
91814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
919653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
920653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
921653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
922653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
923653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
924653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
925653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
926653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
927653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
928653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
929653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
930653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
931653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
932653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
933653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
934653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
935653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
936653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " = " + dataId, null);
937653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
938653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
939f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
940653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
941653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
9423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
94514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
94614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
94714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
9483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
9493296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
9503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
9515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
9523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
9543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
9575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, rawContactId);
9583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
95914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                setIsPrimary(rawContactId, newPrimaryId, getMimeTypeId());
9603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long rawContactId) {
964e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
965e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
9665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Cursor c = queryData(db, rawContactId);
9673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
968e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
96914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
970f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
971e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
972e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
973e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
974e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
9753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
9763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
9773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
9783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
979e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
980e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
981e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
982e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
983e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
984e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
985e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
986e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
987e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
9883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long rawContactId) {
99114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return db.query(DataDeleteQuery.TABLE, DataDeleteQuery.CONCRETE_COLUMNS,
99214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId +
99314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
9943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
9953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
99725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
9983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String bestDisplayName = null;
99925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
100025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
100167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Cursor c = db.query(DisplayNameQuery.TABLE, DisplayNameQuery.COLUMNS,
10025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId, null, null, null, null);
10033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
10043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                while (c.moveToNext()) {
10053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String mimeType = c.getString(DisplayNameQuery.MIMETYPE);
1006a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1007a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    // Display name is at DATA1 in all type.  This is ensured in the constructor.
1008a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    String name = c.getString(DisplayNameQuery.DATA);
1009a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    if (TextUtils.isEmpty(name)
1010a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                            && Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
1011a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        name = c.getString(DisplayNameQuery.TITLE);
10123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
1013a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    boolean primary = StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
1014a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        || (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
10153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
101601911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                    if (name != null) {
101725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        Integer source = sDisplayNameSources.get(mimeType);
101801911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                        if (source != null
101901911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                && (source > bestDisplayNameSource
102001911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                        || (source == bestDisplayNameSource && primary))) {
102125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                            bestDisplayNameSource = source;
10223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            bestDisplayName = name;
10233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        }
10243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
10253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
10263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
10283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
10293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
103125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
1032285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1033285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateDisplayName(db, rawContactId);
1034285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
10353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1036a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1037a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1038a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return true;
1039a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1040622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1041622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1042622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1043622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
1044622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1045622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1046622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1047622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
1048622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=" + dataId,
1049622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    null, null, null, null);
1050622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1051622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1052622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1053622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1054622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        values.put(key, cursor.getString(i));
1055622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1056622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1058622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1059622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1060622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1061622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1062622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
10663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
10683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1073622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
10743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1075622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
10763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1077622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
10783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1082622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
108314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
108414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
108514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1086f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1087f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
108825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
108914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
109014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
109114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
109214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
109314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1094f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1095622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1096622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1097cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1098622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1099622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
110014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1101f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
110214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1103f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
1104f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
1105f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
1106f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
110714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
110825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
110914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
111014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
111114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
111214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
111314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
111414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
111514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
111614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
111714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1118f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
111925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
112014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
11213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1124622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
11253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1126622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1127622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1128622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
11303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1131622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1132622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1133622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1134622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1135622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1136622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1137622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
113867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1139622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
114067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
114167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1142622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1143622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
11448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1145622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1146622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
114767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
114867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
114967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
115067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
115167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
115267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
11538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1154622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
1155622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(name);
1156622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
1157622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1158622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1159622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1160622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1161622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1162622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1163622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1164622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1165622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1166622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1167622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1169622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1170622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1171622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1172622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1173622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1174622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1175622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1176622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1177f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1178622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1179622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1180622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1181f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1182622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1183622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1184622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1185622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1186622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1187622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1188622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1189622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1190622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1192622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1193622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1194622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1195622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1196622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1197622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1198622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1199622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
120067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
120167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
120267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
120367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1204622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1205622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1206622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1207622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1208622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1209622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
121067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
121167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
121267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1213622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1214622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1215622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
12163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
12213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
12233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
12243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
12263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
12273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
12283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
12293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1233622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1234622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1235622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
12363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1237622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1238622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1239f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1240622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1241622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1242622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1243f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1244622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
12453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1246622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1247622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1248622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1249622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1250622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1251622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1252622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1253622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
12543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1255622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1256622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1257622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1258622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1259622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
12603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
12643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
12663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
12673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1271a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1272a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1273a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1274a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1275a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
127625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1277a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1278a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
12793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
128214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1283f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1284a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1285a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1286a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
128714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
128814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1289f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
129014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1292a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
1293a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
129414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
129514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
129714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1298a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
129914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
130014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
130114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
130225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1303a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
130414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
130514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
130614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
130714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
13083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
13093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
13103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
13113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
13123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
13133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
13143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
13153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1316a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1317a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1318a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1319a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1320a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
13213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1323e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1324e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1325e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1326e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1327e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1328e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1329e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
13305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
133114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
133214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
133414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
133525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1336f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
133714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
133814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
133914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
134014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
134114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1342f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
134314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
134414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
134514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
134614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1347f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
134814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1349f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1350f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
135125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
135214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
135314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
135514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
135614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
135714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
135814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
136014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1361f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
136225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
136314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1364e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1365e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1366e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1367e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1368e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1369e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1370e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1371e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1372e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1373e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1374e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1375e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1376e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1377e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
137814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
137914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
138014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
138114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
138214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
138314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
138414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
138514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
138614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
138714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
138814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
138914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1391f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
139214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
139314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
139414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
139614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1397f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
139814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
139914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
140014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
140114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1402f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
140314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1404f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1405f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
140625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
140714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
140814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
140914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
141014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
141114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
141214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
141314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
141514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1416f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
141725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
141814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
141914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
142014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
142114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
14223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
14233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
14253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
14263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
14295ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
14300b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
14310b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
14320b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
14330b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1434653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
14350b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1436653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
14370b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1438285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
143925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
14400b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
14410b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
14420b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1443653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1444653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1445653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1446653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1447653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1448f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
144914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
145014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
14510b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
14520b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
14530b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1454653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1455f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
1456653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
14570b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1458285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
145925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
14600b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1461f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
14620b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
146314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
146414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
146614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
146714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
146814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
146914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
147014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
147114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
147214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1473285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
147425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
147514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1476653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1477653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1478653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1479e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1480e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1481e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1482e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1483653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1484653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1485653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1486e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1487653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1488653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1489e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1490653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
14915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1492653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1493e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1494653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1495653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
1496653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=" + dataId, null);
1497e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
14983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
15013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
15023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
15033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
15043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
15053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
15063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
15073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
15083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
15093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
15103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
15113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
15123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
15133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
15153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1516653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1517653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1518653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1519653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1520653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1521653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1522653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1523653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1524653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
15250be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
15260be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
15270be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1528653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1529653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1530653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1531653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1532f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
153314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1534653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1535f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
15360be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
15370be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
15380be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
15390be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
15400be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
15410be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
15420be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
15430be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
15440be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
15450be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
15460be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
15470be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1548b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
15490be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1550b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
15510be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1552653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1553653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1554653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1555653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1556653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1557653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1558653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1559653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1560653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1561653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1562653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1563653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1564653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1565653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1566653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1567653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1568653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1569653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1570653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1571653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1572653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1573653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1574653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1575653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1576653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId);
1577653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1578653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1579653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1580653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1581a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1582a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1583a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1584a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1585a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1586653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1587653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1588a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1589a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1590a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1591a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1592a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1593a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1594a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1595a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1596a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1597285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1598285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1599285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1600a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1601a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1602a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1603a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1604a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1605f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1606a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1607f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1608a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1609a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1610a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1611a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1612a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1613a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1614a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1615a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1616a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1617a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1618a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1619a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1620a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1621a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1622a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1623a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1624a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1625a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
16263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
162753056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
1628b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
162931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
16304097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1631f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1632f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache =
1633f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            new HashMap<String, SoftReference<String[]>>();
1634622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1635622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1636622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1637f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1638a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1639a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
164020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
164120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1642ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
164373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private boolean mImportMode;
164473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1645b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mInsertedRawContacts = Sets.newHashSet();
1646b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1647b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1648de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
16491a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
16501a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
165181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
165281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
1653a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
165453056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
1655a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1656a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1657a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1658a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
1659a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
166053056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
166153056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
1662a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
16634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
16644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
16654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1666de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1667ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1668ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1669ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1670ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1671ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1672ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1673ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
167435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1675ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1676de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1677b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1678a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1679b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1680b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper, mAggregationScheduler);
16810e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1682a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1683b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1684653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1685c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1686653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1687653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1688653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1689653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1690653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1691c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1692653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1693653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1694653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1695653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1696653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1697653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1698653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1699653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1700653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1701653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1702653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1703ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate = db.compileStatement(
1704ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "UPDATE " + Tables.CONTACTS +
1705ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
1706ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton                "WHERE " + Contacts._ID + "=?");
1707a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
170825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate = db.compileStatement(
170925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
171025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
171125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
171225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
17133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
171473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
171573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                + RawContacts.DIRTY + "=1 WHERE " + RawContacts._ID + "=?");
171673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1717a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mLastStatusUpdate = db.compileStatement(
1718a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.CONTACTS
1719a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                + " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1720a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1721a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1722a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1723a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1724a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1725a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1726a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1727a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1728a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
17290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
17300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1731a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " LIMIT 1)"
1732a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                + " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1733e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1734622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        final Locale locale = Locale.getDefault();
173528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
173628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
173728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
173828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
1739622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1740622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                locale);
1741f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1742622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
17434097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1744f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1745f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1746f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1747f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1748f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1749f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1750f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1751a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateInsert = db.compileStatement(
1752a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1753a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1759a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1760a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateReplace = db.compileStatement(
1761a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1762a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
17630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
17640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
17650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
17660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
17670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
17680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1769a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1770a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateAutoTimestamp = db.compileStatement(
1771a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
17730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1774a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
17750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
17760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
17770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        mStatusAttributionUpdate = db.compileStatement(
17780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
17790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
17800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
17810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
17820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1783a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1784a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        mStatusUpdateDelete = db.compileStatement(
1785a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1786a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1787a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
17883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
17893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1790e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
17913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
17923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
179367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
179467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
17953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
17963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
179714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
17983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
17993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1800622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1801622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1802a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1803a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
18043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
18053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1806568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            importLegacyContactsAsync();
18073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1808568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1809c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        verifyAccounts();
181070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
1811f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1812f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
18131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
18144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
18154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1816c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
1817c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1818c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1819c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
1820c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
182131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1822de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
1823b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
1824b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
182531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
182631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1827285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    /* package */ ContactAggregationScheduler getContactAggregationScheduler() {
1828285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mAggregationScheduler;
1829285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1830285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1831013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1832013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1833013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1834013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
18353d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
18363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
18383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1840568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1841568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1842568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1843568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1844568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1845568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
1846568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
1847568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1848568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
1849ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        mAccessLatch = new CountDownLatch(1);
1850568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1851568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
1852568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
1853568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
1854568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                if (importLegacyContacts()) {
1855568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1856568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    /*
1857568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * When the import process is done, we can unlock the provider and
1858568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * start aggregating the imported contacts asynchronously.
1859568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     */
1860ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch.countDown();
1861ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1862568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    scheduleContactAggregation();
1863568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
1864568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1865568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
1866568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1867568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
1868568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1869568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
18703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
1871568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1872568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        if (importLegacyContacts(importer)) {
18733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
18743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
18753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
18763d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
18773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18783d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
18793d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
18803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
18833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1884568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
18850e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
18863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
188773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mImportMode = true;
18883d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
18893d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
18900e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff            mContactAggregator.setEnabled(aggregatorEnabled);
18913d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
18923d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
18933d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
18943d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
189573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } finally {
189673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mImportMode = false;
18973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
18983d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
18993d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1900a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
1901a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
1902a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
1903a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
1904a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
1905a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1906a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
1907a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1908a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1909a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1910a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1911a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1912a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1913b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
1914a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1915a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1916568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1917568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1918568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1919568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1920568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1921568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1922568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1923568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
1924ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
1925ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
1926ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
1927ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
1928ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
1929ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1930ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
1931ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
193281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
1933ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
1934ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1935568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1936568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1937568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1938568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1939568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1940568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1941568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1942568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1943568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1944568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1945568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1946568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1947568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1948568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1949568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1950568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1951568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
1952568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1953568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1954568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1955568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1956568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1957568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1958568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
1959568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1960568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1961568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1962568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
19634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1964285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1965bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1966b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1967b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1968285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
19691ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1970b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1971b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1972b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1973b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
1974285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
1975b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
1976df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
1977285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1978285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1979285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1980285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
1981bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1982b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1983b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1984285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1985b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
19861ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
19871a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
19881a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
1989b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
19901a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
1991b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1992b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1993b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1994bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1995b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1996b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1997b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long rawContactId : mInsertedRawContacts) {
1998b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mContactAggregator.insertContact(mDb, rawContactId);
1999285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
2000b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2001b5a4add17815167d20a90645779df34cdf45280dFred Quintana        String ids;
2002b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
2003b5a4add17815167d20a90645779df34cdf45280dFred Quintana            ids = buildIdsString(mUpdatedRawContacts);
2004b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mDb.execSQL("UPDATE raw_contacts SET version = version + 1 WHERE _id in " + ids,
2005b5a4add17815167d20a90645779df34cdf45280dFred Quintana                    new Object[]{});
2006b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2007b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2008b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
2009b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
2010b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().update(mDb, id, entry.getValue());
2011b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2012b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2013b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2014b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2015b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2016b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private String buildIdsString(HashSet<Long> ids) {
2017b5a4add17815167d20a90645779df34cdf45280dFred Quintana        StringBuilder idsBuilder = null;
2018b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2019b5a4add17815167d20a90645779df34cdf45280dFred Quintana            if (idsBuilder == null) {
2020b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder = new StringBuilder();
2021b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append("(");
2022b5a4add17815167d20a90645779df34cdf45280dFred Quintana            } else {
2023b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append(",");
2024b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
2025b5a4add17815167d20a90645779df34cdf45280dFred Quintana            idsBuilder.append(id);
2026b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2027b5a4add17815167d20a90645779df34cdf45280dFred Quintana        idsBuilder.append(")");
2028b5a4add17815167d20a90645779df34cdf45280dFred Quintana        return idsBuilder.toString();
2029285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2030285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2031285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2032cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
203381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
203481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
203581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
203681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
203781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
203881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
203981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2040cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2041568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2042568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected void scheduleContactAggregation() {
2043dee54bb86f3608730f0b9f37d8982a7f6b280a85Dmitri Plotnikov        mContactAggregator.schedule();
2044568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2045568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2046285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2047285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mInsertedRawContacts.contains(rawContactId);
2048285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2049285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
20503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
20513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
20523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
20533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
20543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
20553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
20563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
20573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
20583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
20594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2060de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2061bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2062b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "insertInTransaction: " + uri);
2063b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2064f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2065f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2066f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2067f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2068a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2069a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
207035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2071a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
207235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2073b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
207435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
207535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2076d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2077d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
20786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
20796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
20806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
20815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2082f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
2083d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                id = insertRawContact(values, account);
2084f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2085a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2086a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2087a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
20885ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
20895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2090f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2091f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2092a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2093a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2094a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2095a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2096f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2097f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2098a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2099a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2100a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2101ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2102ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
21035aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertGroup(uri, values, account, callerIsSyncAdapter);
2104f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2105ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2106ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2107ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2108eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
21095aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
211043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2111eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2112eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2113eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
211482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
211582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
21161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
21171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
21181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2119a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
212081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2121f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2122a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2123a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
21247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
21257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
21267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2128de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2129a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2130a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2131a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2132035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
2133035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
2134035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
2135035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
2136035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
21377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2138035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
2139035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
21406cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
21416cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2142035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
2143035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
2144035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
2145035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
2146035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
2147035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
2148035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2149035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
2150df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_NAME, account.name);
2151df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_TYPE, account.type);
2152035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2153035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
21547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
21557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2157d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
21586bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
21596bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
21606bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
21616bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2162d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2163de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
21646bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
21656bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
21666bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2167a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2168a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2169a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2170f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
2171a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2172a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2173d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertRawContact(ContentValues values, Account account) {
2174a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
2175d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        overriddenValues.putNull(RawContacts.CONTACT_ID);
2176f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
21777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
21787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
21797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
21803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
21813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
21823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            overriddenValues.put(RawContacts.AGGREGATION_MODE,
21833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                    RawContacts.AGGREGATION_MODE_DISABLED);
21843d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
21853d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2186023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        long rawContactId =
2187023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov                mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, overriddenValues);
2188023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
2189285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2190285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2191285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.add(rawContactId);
2192023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2193a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2194a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2195a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2196a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2197a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2198a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2199a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2200a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2201f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2202a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2203de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2204de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
220567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2206de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
220720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2208de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2209de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2210de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2211b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2212de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2213de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2214508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2215de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2216de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2217de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2218de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2219de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
22204097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2221b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2222de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2223a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2224a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2225a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2226f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2227de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2228a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2229b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2230a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2231a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2232a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2233a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2234a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
22354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
22364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
22378e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
22388e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
22398e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
22408e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
22418e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2242b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2243f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
22448e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
22458e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
22468e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22478e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2248421782cb554e5050cf62a86b98df6520038dcd15Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId);
2249f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22508e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
22518e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
22528e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2253b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2254f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
22558e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
22568e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
22578e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2258f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22598e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2260f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2261c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2262b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
22638e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2264f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
22658e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2266f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2267f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2268f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2269a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
22705ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
22719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
22729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
22735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
22749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
22759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
22769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
22779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
22789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
22795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId) {
22809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
22816cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, RawContacts._ID + "="
22825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                + rawContactId, null, null, null, null);
22839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
22849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
228567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountName = c.getString(ContactsQuery.ACCOUNT_NAME);
228667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountType = c.getString(ContactsQuery.ACCOUNT_TYPE);
22879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
22889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
22899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
22909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
22919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
22929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
22939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
22949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
22959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
22969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
22979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
22989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
22999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
23009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
23015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
23026cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
23039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2304df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
23059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
23069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
23079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
23089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
23099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2310df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2311df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
23129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
23139261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
23149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
23159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
23169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
23179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
23189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
23199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
23209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
23219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
23229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
23239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
23249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
23259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
232620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
232720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2328f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
232920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
233020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2331de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2332de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
233314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2334de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2335de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
233614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
233714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2338a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2339a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2340f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
234188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
2342a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    if (rowHandler.isAggregationRequired()) {
2343a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        triggerAggregation(rawContactId);
2344a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    }
234588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
234620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
234720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2348de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
234920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
235020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
235120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
235220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
235320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
235488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
235588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
235688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
235720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2358f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
235988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
236088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
236114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=" + dataId, null,
236214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                null);
2363f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
236420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
236520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
236620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
236720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
236820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
236914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
237020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
237120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
237220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
237320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
237420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
237520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
237620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
237720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
237820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
23797a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
238020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
238120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
238220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2383a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2384a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = rowHandler.delete(mDb, c);
23858e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2386a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            if (rowHandler.isAggregationRequired()) {
2387a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                triggerAggregation(rawContactId);
2388a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            }
23898e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return count;
239020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
239120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
239220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
239320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
239420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
239520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2396ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2397ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2398b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, Account account,
2399b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            boolean callerIsSyncAdapter) {
2400ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
2401ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
2402ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
2403ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2404ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2405ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
240667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE);
240767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2408b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            overriddenValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
240967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
241067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        overriddenValues.remove(Groups.RES_PACKAGE);
2411ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2412f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
241373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            overriddenValues.put(Groups.DIRTY, 1);
241473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
241573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2416ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
2417ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
24181a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (overriddenValues.containsKey(Groups.GROUP_VISIBLE)) {
24191a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2420ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2421ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2422ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2423ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2424ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
24255aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2426e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
24275aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
24281a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
24291a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2430e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
24311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2432e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2433e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2434e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2435ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
243682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
24371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
243882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
243982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
24400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
24414dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
24424dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
24430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
244482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
24454dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
24464dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
24474dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
24484dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
24491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
24501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2451dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2452dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
245382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2454f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
2455dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2456dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2457dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2458f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=");
2459f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            mSb.append(dataId);
24601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2461dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2462dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
24630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
24640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
24650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
24660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2467dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
2468dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
2469dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
2470dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
2471f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2472f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
2473f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
2474f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
2475f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
2476f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
2477f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
2478f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (")
2479f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2480f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(",")
2481f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2482f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(")" + " AND " + Data.DATA1 + "=");
2483f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2484f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND ((" + DataColumns.MIMETYPE_ID + "=")
2485f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2486f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2487f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol);
2488dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2489f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2490f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2491dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2492f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=")
2493f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdEmail)
2494f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append("))");
2495dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
2496f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=")
2497f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(mMimeTypeIdIm)
2498f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.PROTOCOL + "=")
2499f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(protocol)
2500f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                        .append(" AND " + Im.DATA + "=");
2501f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(mSb, handle);
2502dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
2503f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
2504f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(mSb, customProtocol);
2505dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
2506dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
25071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
250882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
2509f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=")
251082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                        .append(values.getAsLong(StatusUpdates.DATA_ID));
2511dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
251270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
2513f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
251470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
25151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
25161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2517de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
2518c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    mSb.toString(), null, null, null,
2519c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
25201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
252167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
25225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2523e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
25241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
25251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
25261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
25271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
25281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
252931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
253031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
253131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
25321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
25331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
253482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
2535a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
2536a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
2537a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
2538a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
2539a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
2540a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2541a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
254282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
2543a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
2544a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
254582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
254682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
254782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
254882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
254982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
2550a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
255182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
255282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
25531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2554a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
2555a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
2556a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2557e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
25580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
255982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
256082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
25610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
25620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
25630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
25640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
25650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
25660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
25670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
25680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
25690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
25700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
25710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
25720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2573a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
2574a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
2575a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
257682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
257782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
2578a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
2579a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
25800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 3, status);
25810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 4, resPackage);
25820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 5, iconResource);
25830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DatabaseUtils.bindObjectToProgram(mStatusUpdateReplace, 6, labelResource);
2584a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
2585a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
2586a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2587a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
2588a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
25890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 2, status);
25900a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 3, resPackage);
25910a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 4, iconResource);
25920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateInsert, 5, labelResource);
2593a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
2594a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
2595a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
25960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
2597a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
25980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 2, status);
2599a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
26000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusUpdateAutoTimestamp, 4, status);
2601a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
26020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
26030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 1, resPackage);
26040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 2, iconResource);
26050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(mStatusAttributionUpdate, 3, labelResource);
26060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
26070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
2608a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
2609e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2610e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
2611bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
2612a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
2613a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
2614a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
2615a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
2616a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
2617a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
2618a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
26191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
26201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
26214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2622de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2623bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2624b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2625b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2626b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2627f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2628f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2629508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2630508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
263135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2632b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
263335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2634b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2635b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2636b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2637b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2638b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2639b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2640cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2641cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2642cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2643cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2644cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2645d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2646d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2647cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
26486bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
26496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
26502e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
26512e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
26522e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
26532e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
26542e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
26552e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
26562e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
26572e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
26582e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
26592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
26602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
26612e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
26622971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
26632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
26642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2665e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
26662971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
26672971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
26682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2669f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        numDeletes += deleteRawContact(rawContactId, callerIsSyncAdapter);
26702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
26712971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
26722971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
26732971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
26742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
26752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
26762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
26775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
26782971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2679f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteRawContact(rawContactId, callerIsSyncAdapter);
2680508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2681508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
268220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2683f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2684944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2685f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
268620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
268720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
268848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
268948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
269048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
269148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2692508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2693f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2694f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteData(Data._ID + "=" + dataId, null, callerIsSyncAdapter);
2695ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2696ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2697ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2698f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
26995aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
27002971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
27012971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
27022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
27032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
27042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2705e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
27062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
27072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
27085aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
27092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
27102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
27112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
27122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
271381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2714f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
271581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
27162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2717508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2718508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2719eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
272043880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
27215aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteSettings(uri, selection, selectionArgs);
2722eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2723eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
272482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
27250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
27261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
27271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
272881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
272981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
27303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
273181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2732508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
27334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
27344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
273546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static boolean readBooleanQueryParameter(Uri uri, String name, boolean defaultValue) {
27362971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        final String flag = uri.getQueryParameter(name);
27372971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        return flag == null
27382971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                ? defaultValue
27392971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                : (!"false".equals(flag.toLowerCase()) && !"0".equals(flag.toLowerCase()));
274094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
274194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
27421c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
2743b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
274494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2745de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
274694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
274794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
274894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
274994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2750f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2751de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
275294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
275394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
275494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2755f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2756de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
275794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
275894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
27591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
276094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
276194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
276294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
27635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
2764e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
27651a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
2766e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2767e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2768e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2769cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
2770cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2771cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
2772cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2773cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2774cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2775cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
2776cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2777cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2778cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2779cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2780cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2781cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2782cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2783cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2784f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    public int deleteRawContact(long rawContactId, boolean callerIsSyncAdapter) {
2785f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
278614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2787de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
278833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2789b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
2790cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
279133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
279233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
279333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
27940a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
27950a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        // TODO delete from both tables: presence and status_updates
27960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
27970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
27980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
2799cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
280081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
280181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2802cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2803cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2804cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2805cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2806cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2807cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2808cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
2809cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2810cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
28114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2812de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2813de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2814bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2815b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2816b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2817b5a4add17815167d20a90645779df34cdf45280dFred Quintana
281835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
281900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
282000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2821b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2822b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
2823b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Object data = values.get(ContactsContract.SyncStateColumns.DATA);
2824b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
2825b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2826b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2827b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2828f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2829f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
283000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
283135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2832b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2833b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2834b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2835b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2836b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2837b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2838b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2839b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2840b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
2841b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2842b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
284335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2844d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
28458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
284600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
284700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
284800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2849d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
28508c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
2851c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2852c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2853c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
28542e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
28552e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
28562e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
28572e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
28582e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
28592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
28602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
28612e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
28622e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
28638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
28642e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
28652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
28662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
28677d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
28687d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
28697d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
28707d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
28717d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
28727d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
28737d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
28747d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
28757d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
28767d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
287720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2878944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2879f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
288081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2881f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
288281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
288320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
288420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2885c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
288648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
288748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
288848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
288948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2890f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
289181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2892f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
289381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
289400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
289500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
28967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
28975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
28985ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
28994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
29007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
29017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
29027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
29035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
290433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
29054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
29064529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId
29074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
29084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
290927f039b535f98c1cb1a31207047003235ddaed15Dmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId, null);
29104529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
29117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
29127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
29137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2914ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
29155aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
2916f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
291781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2918f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
291981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2920ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2921ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2922ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2923ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2924ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
292573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                String selectionWithId = (Groups._ID + "=" + groupId + " ")
292673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
29275aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
29285aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
292981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2930f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
293181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2932ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2933ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2934ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2935127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2936de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2937b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2938b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2939b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2940eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
29415aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateSettings(uri, values, selection, selectionArgs);
294243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2943eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2944eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2945eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
294681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
294781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2948f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
294981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
295000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
295100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
295200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
29534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
29544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
29555aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
2956f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
295773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
295873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2959f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
296073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
296173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
296273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
296373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
296473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
296573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
296673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
296773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2968ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
29691a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
29701a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
297194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
29726ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
2973627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
29746ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            final long groupId = ContentUris.parseId(uri);
29756ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
29766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    Groups.ACCOUNT_TYPE}, Groups._ID + "=" + groupId, null, null,
29776ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
29786ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
29796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
29806ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
29816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
29826ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
29836ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
29846ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
29856ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
2986627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
29876ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
29886ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
29896ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
29906ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
29916ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
29926ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
29936ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
29946ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
299594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
299694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
299794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2998b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
2999b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3000e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
30011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
30021a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3003e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3004e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3005e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3006e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
30074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
30084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
30094529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
30104529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
30114529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
301273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
30134529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3014b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
301551bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
30164529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
30174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
30184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
30194529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
30204529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
30214529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
30224529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
30234529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
30244529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
30254529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
30264529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
30274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
30284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
30294529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
30304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
303119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final String selection = RawContacts._ID + " = " + rawContactId;
303219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
303319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
303419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
303519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
303619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
303719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    null, null, null, null);
303819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
303919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
304019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
304119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
304219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
304319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
304419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
304519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
304619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
304719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
304819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, null);
30495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3050433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
30514529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3052433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3053285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
3054285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateLookupKey(mDb, rawContactId);
3055285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
305619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
305719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
305819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                mInsertedRawContacts.add(rawContactId);
305919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
30605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
30615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
306233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
306333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3064321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3065f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
306620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
306720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
306820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
30695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
307020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
307120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
307220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
307320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
307420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3075b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
307620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
307720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
307870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
307970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
308020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
308120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
308220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
308370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
308420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
308570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
308620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
308770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
308820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
308970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
309020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
309120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3092653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
309320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3094653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3095653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
309614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3097653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3098653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3099f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
310020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3101653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3102653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
310320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
310420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3105653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
310620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
310720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3108f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3109653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3110653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3111321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3112653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
311314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3114a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3115f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
31168e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
3117a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
3118a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
3119a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
31208e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
3121653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return 1;
3122321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3123321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
31248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
31258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
31268c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3127b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
31288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
31298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
31308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
31318c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
31328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
31338c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
31348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
31358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
31368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
31378c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
31388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
31398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
31418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
31428c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31438c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3144d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
31458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3146b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3147d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3148b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3149d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3150b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3151d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3152b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3153d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3154b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3155d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3156d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3157d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
31588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3159d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3160d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3161d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
31628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3163c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
31648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3165c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3166c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
31678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=" + contactId, null);
31688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
31708c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
31718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3172b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
31738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3174b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
31758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3176b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
31778c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3178b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
31798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3180b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
31818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
31828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
31838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=" + contactId, null);
3184f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3185d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3186ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton    public void updateContactLastContactedTime(long contactId, long lastTimeContacted) {
3187ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
3188ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.bindLong(2, contactId);
3189ba965ceeb86dd9404d43f418daae357bc4afbdcdJeff Hamilton        mContactsLastTimeContactedUpdate.execute();
3190d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
3191d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3192127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3193127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
31940c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
31950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
319680c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
31970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
31980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
31990c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
32000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
32010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
32020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
32030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3204b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3205127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
32060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
32070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
32080c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId1 + " AND "
32090c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId2, null);
32100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
32116bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
32126bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
32130c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
32140c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
32150c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
32160c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3217127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3218127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3219dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
3220dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
3221dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3222b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
32230c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
32240c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
3225b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
32260c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
3227127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3228127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3229127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3230127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3231b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3232b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
323370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3234b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
323570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        if (mDb == null) return;
323670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
3237627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
3238627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        boolean hasUnassignedContacts[] = new boolean[]{false};
323970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
324070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3241627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3242627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.RAW_CONTACTS, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE);
3243627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3244627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.GROUPS, Groups.ACCOUNT_NAME, Groups.ACCOUNT_TYPE);
3245627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts,
3246627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE);
324748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3248627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3249627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // in the existingAccounts set will be extra accounts whose data must be deleted.
3250627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3251627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3252627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
325370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
325470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
325570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
32565f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
3257627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                String[] params = new String[] {account.name, account.type};
3258627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3259627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.GROUPS +
3260627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3261627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3262627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3263627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.PRESENCE +
3264627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3265627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                "SELECT " + RawContacts._ID +
3266627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
3267627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3268627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3269627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3270627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.RAW_CONTACTS +
3271627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3272627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3273627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3274627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.SETTINGS +
3275627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3276627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3277627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3278627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3279627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            if (hasUnassignedContacts[0]) {
3280627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3281627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                Account primaryAccount = null;
3282627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                for (Account account : accounts) {
3283627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    if (isWritableAccount(account)) {
3284627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        primaryAccount = account;
3285627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        break;
3286627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    }
3287627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3288627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3289627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (primaryAccount != null) {
3290627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    String[] params = new String[] {primaryAccount.name, primaryAccount.type};
3291627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3292627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3293627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.RAW_CONTACTS +
3294627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + RawContacts.ACCOUNT_NAME + "=?,"
3295627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + RawContacts.ACCOUNT_TYPE + "=?" +
3296627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
3297627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL", params);
3298627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3299627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // We don't currently support groups for unsynced accounts, so this is for
3300627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // the future
3301627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
3302627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.GROUPS +
3303627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + Groups.ACCOUNT_NAME + "=?,"
3304627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + Groups.ACCOUNT_TYPE + "=?" +
3305627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " IS NULL" +
3306627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + Groups.ACCOUNT_TYPE + " IS NULL", params);
3307627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
330870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
3309627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3310b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
331170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
331270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
331370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
331470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
331570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
3316619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3317619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
3318627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
3319627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
3320627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    private void findValidAccounts(Set<Account> validAccounts, boolean[] hasUnassignedContacts,
3321627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            String table, String accountNameColumn, String accountTypeColumn) {
3322627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        Cursor c = mDb.rawQuery("SELECT DISTINCT " + accountNameColumn + "," + accountTypeColumn
3323627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                + " FROM " + table, null);
3324627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
3325627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
3326627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (c.isNull(0) && c.isNull(1)) {
3327627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    hasUnassignedContacts[0] = true;
3328627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                } else {
3329627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
3330627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
3331627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3332627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
3333627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
3334627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
3335627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
3336627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3337627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
3338622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
3339622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
334067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
334167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
334267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
334367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
334467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
334567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
334667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
334767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
334867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
334967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
335067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
335167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
3352dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
3353622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
3354dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
3355dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
3356622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
3357622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
3358dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
3359622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
3360622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
33614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
33624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
33634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
3364bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3365bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
3366bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
33670b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
3368b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
336935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3370d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
33711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
3372c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
3373c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3374619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
3375619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
3376a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
33774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
337835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3379b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
338035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
338135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3382d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3383763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3384619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
3385619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
3386619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3387d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
33884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3389763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
33904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + contactId);
33916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
33926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
33936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
33945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
33955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
33965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
33975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
33985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
33995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
34005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
34015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
34025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
34035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
34045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
3405763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
34065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=" + contactId + " AND " +
34075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            Contacts.LOOKUP_KEY + "=");
34085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhereEscapeString(lookupKey);
34095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, selectionArgs, sortOrder,
34105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
34115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
34125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
34135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
34145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
34165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
34175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3418763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
34195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
34205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
34215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
34225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3423f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
3424f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
3425f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
3426763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
3427f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
3428f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
3429f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
3430f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
3431f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
3432ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
3433763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3434ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
34354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
34364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3437e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
34385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
34394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sb.toString());
3440ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
3441ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3442ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3443ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
3444ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
3445ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
34464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3447ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3448d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
34494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
34504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3451e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
34525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
34534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
34544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
34554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3456763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3457ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
34585e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
34595e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
34605e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
34615e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
34625e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
34635e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
34645e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
34654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
34664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3468d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
34705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
34714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3472d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3473d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3474d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3475763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
34764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
34774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3478d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
34795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
34805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
3481d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3482d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
34834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3484d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3485d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3486d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3487d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
34884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
34894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3490d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3491d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3492d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3493d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3494d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3495d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3496ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3497763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
3498b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
349971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
35004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3501b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3502b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3503b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3504b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3505d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
35064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
350782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
35084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
35096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
35106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
351100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3512ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
35133653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
351482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
35153653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
35163653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
35173653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
35183653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
35193653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
35204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
352182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
352289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
35232815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
35242815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
35252815f58f72f109790585931f601a63ddc02536a5Evan Millar
352648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
352782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
352848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
352948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
353048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
353148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
353248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3533ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
353482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
353589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3536ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
35374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
35384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
35395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
35405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
35415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
35425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
35435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
35445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
354520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
35465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
35475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
35485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
35495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
35505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
35515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
35525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
35535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
35545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
35555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
35565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
35575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
35585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
35595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
35605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
35615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
35625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
35634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
3564ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
35655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3566a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3567a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3568a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
3569ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3570ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3571ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
35724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
357382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
357489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
35764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
35774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
357848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
357982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
358048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
358148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
358248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
358348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
358448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
35855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
358682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
358789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
35895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + Email.DATA + "=");
35904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhereEscapeString(uri.getLastPathSegment());
35914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3592ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3593ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3594ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
35955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
359682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
359789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
35985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
35995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
36005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
36015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
36025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
360320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
360420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
360520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
360620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            sb.append(Data.RAW_CONTACT_ID + " IN ");
360720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, null, false);
360820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                            sb.append(" OR ");
360920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
36105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
36115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
36125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(Email.DATA + " LIKE ");
36131e530df9f7e496dc47f77d4323c89bd413b79b64Dmitri Plotnikov                    sb.append(DatabaseUtils.sqlEscapeString(filterParam + '%'));
36145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
36155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
36165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
36175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
3618a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
3619a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
3620a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
36215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
36225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3624ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
362582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
362689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
362789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3628ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3629ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3630ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
363148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
363282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
363348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
363448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
363548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
363648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
363748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
363848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
36395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
3640763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
36414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
36424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
36434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
36455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
3646763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
364789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
36484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
36494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
36504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
36525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
365382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
365489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=" + rawContactId);
3655e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3656e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3657e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3658e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
365982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
3660e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3661e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3662e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
36634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
366482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
366582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + ContentUris.parseId(uri));
3666a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3667a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3668a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3669a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
36704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3671a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3672a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3673a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3674e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
3675a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3676a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3677e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3678b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
3679e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3680e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
3681e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3682e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3683e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3684a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3685a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3686a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3687ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
3688b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3689ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
369089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3691ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3692ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3693ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3694ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3695ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
3696b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
3697ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
369889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(Groups._ID + "=" + groupId);
3699ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3700ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3701ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3702ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
3703b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
3704ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
370589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
370689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3707ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3708ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3709ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3710b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
37110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3712b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3713b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3714b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3715b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
371631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3717d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
37182d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
37192d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
37202d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
37212d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
372231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3723d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3724d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
372531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
372631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
372731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
372831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3729763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
37307581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
37317581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
37322d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
373331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
373431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3735eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3736eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3737eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
373889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3739e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3740e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3741e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3742b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
3743e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
374482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3745b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3746e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3747e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
374882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
3749b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3750e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3751e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3752e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3753eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3754eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3755eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
375682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
37570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
37585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
37595ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
37605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
376182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
37620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
37630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri));
37645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
37655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
37665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3767c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3768a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3769c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3770c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3771c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
3772b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3773b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
3774c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3775c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
37761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
3777b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
37791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37801b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
37811b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
3782b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
37841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
37851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
37871b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
3788b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
37901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
37911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37921b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
37931b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
3794b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
37951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
379671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
37971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
37981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
37991b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
380046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
380146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
380246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
380346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
380446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
380546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
380646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
380746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
380846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
380946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
381046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
381146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
38124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
3813f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3814c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
38154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
38164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
38185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
38195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
38215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
38225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
3823038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
3824038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
3825038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
3826038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
38275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
38285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
38294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
38304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
38314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
38324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
38334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
38344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
38355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
38365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
38375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
38385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
38405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (contactId == -1) {
38415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
38425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
38455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
38465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
38485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
38495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
38515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
38525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
38535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
38545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
38555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
38565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
38585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
38595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
38605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
38615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
38625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
38645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
38655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int sourceIdCount = 0;
38665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
38675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
38685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
38695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sourceIdCount++;
38705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
38715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (sourceIdCount == 0) {
38745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
38755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
38785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
38795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
38805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
38815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
38825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
38835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
38845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
38855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
38865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
38885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
38895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
38905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
38915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
38925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
38935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
38945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
38955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
38965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
38975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
38985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
38995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
39005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
39015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
39025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
39035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
39045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
39055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
39065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
39075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
39085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
39095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
39105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
39135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
39145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupByDisplayNameQuery {
39165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
39175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
39195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
39205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
39215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
39225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
39235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
39245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
39265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
39275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
39285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int NORMALIZED_NAME = 3;
39295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
39305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
39325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
39335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int displayNameCount = 0;
39345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
39355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
39365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
39375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                displayNameCount++;
39385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
39395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
39425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
39435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
39465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
39475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
39485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
39495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
39505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
39515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
39525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
39535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
39545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
39565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
39575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
39585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
39605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
39615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
39625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
39635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
39645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
39655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
39665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
39675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
39685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
39695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
39705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
39715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
39725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
39735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
39745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
39755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
39765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
39775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
39785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
39795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
39825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
39835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
39855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
39865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
39875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
39885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
39895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
39915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
39925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
39945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
39955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
39965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
39975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
39985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
39995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
40005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
40015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
40025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
40035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
40045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
40055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
40065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
40075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
40085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
40095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
40105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
40115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
40135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
40145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
40155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
40165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
40175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
40185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4019763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
4020763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
402182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4022763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4023763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        String requestingPackage = uri.getQueryParameter(
4024763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4025763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4026d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4027763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4028763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
4029b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
403082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
403182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
403282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
403382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
4034b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
403582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
403682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
403782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
403882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
403982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
40403296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
40413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
4042a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
40433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
404482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
404582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
404682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
404782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
4048ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
4049763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
4050763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
4051763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
4052763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        String requestingPackage = uri.getQueryParameter(
4053763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4054763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4055d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4056763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4057763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
4058763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
4059763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
4060763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
4061763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
4062763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
406346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
406446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        // Note: currently, "export only" equals to "restricted", but may not in the future.
406546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
406646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.FOR_EXPORT_ONLY, false);
406746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
406846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        String requestingPackage = uri.getQueryParameter(
406946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
407046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        if (requestingPackage != null) {
407146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            excludeRestrictedData = excludeRestrictedData
407246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
407346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        }
407446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
407546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setProjectionMap(sRawContactsEntityProjectionMap);
407646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
407746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
407846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
407982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
408082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
408182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4082d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa        // Note: currently, "export only" equals to "restricted", but may not in the future.
4083763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
4084d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa                Data.FOR_EXPORT_ONLY, false);
4085763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4086763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        String requestingPackage = uri.getQueryParameter(
4087763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
4088763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
4089763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            excludeRestrictedData = excludeRestrictedData
4090d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
4091763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
4092763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
4093763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getDataView(excludeRestrictedData));
409482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
409582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
40963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated presence when requested
4097b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
409882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
40993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
410082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
410182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
410282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
41033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated status updates when requested
4104b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
410582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS,
410682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
410782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
410882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
410982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
41103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
41113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
411282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
41133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
4114ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
41153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
41163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual presence when requested
41173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
41183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
41193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdates.DATA_ID + "="
41203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + DataColumns.CONCRETE_ID + ")");
41213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
41223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
41233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual status updates when requested
41243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection,
41253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS,
41263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_RES_PACKAGE,
41273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_ICON,
41283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_LABEL,
41293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_TIMESTAMP)) {
41303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
41313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
41323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + DataColumns.CONCRETE_ID + ")");
41333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
41343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
413582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
413682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
413782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
4138ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
4139ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
41400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
41410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
41420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4143b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
41440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
41450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4146b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
41470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
41480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
41490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
41500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
41510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
4152b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
41530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
41540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
41550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
41560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
41570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
41580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
41590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
41600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
41610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
41620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setTables(sb.toString());
41630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
41640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
41650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
41664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
41674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
41684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
41694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
41704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
41714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
41724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
41734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
41744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
41754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
41764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
41774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
41784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4179e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
4180e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
4181e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
4182e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        if (!TextUtils.isEmpty(accountName)) {
4183e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
4184e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4185e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
4186e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
4187e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
4188e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
4189e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
4190e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
4191e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
4192e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
4193e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
4194e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
4195e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
4196e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
4197e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
41987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
4199c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
4200c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
4201c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
4202c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
4203c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
4204c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private String getLimit(Uri url) {
4205c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limitParam = url.getQueryParameter("limit");
4206c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
4207c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4208c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4209c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
4210c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
4211c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
4212c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
4213c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
4214c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
4215c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4216c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
4217c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
4218c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
4219c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
4220c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4221c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4222c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
42235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
42245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
42255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
42265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
42275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
42285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
42295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
42305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
42315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
42325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
42335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
42345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
42355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
42365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
42375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
42385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
42395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
42405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
42415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
42425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
42435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
42445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
42455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
42465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
42475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
42485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
42495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
42505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
42515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
42525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
425300ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
4254d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
425570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
425670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
42576cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
425870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
425970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
426070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
426170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
4262d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
426370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
426467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
42655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
42665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
4267619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
4268619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
4269619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4270b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
4271b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
4272b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
4273b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
4274d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
4275b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                if (!"r".equals(mode)) {
4276b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
4277b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
4278b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4279b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4280b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4281b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                String sql =
4282b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
4283b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
4284b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=" + contactId;
4285b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
4286b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql, null);
4287d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4288d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4289f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4290d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
4291d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
429214b8a1243ab5c043b35e47527ca1c962064f3771Daisuke Miyakawa                final String selection = Contacts._ID + "=" + contactId;
4293d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4294d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
4295d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
4296d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
4297d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
4298d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                outputRawContactsAsVCard(localStream, selection, null);
4299d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
4300d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4301b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4302b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
4303b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new FileNotFoundException("No file at: " + uri);
4304b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
4305b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
4306b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4307d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
4308d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String VCARD_TYPE_DEFAULT = "default";
4309d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4310d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4311d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
4312d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
4313d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4314d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
4315d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
4316d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
4317d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
4318d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4319d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
4320d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
4321d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4322d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
4323d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
4324d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
4325b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4326d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
4327d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
4328d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
4329d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4330d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
4331d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4332d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4333d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
4334d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
4335d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
4336d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
4337d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
4338d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
4339d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
4340d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
4341d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
4342d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
4343d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4344f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
4345d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        if (!composer.init(selection, selectionArgs))
4346d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
4347d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
4348d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
4349d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
4350d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
4351d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
4352d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
4353d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
4354d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
4355b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
4356bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
4357bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov    private static Account readAccountFromQueryParams(Uri uri) {
4358bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        final String name = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
4359bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        final String type = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
4360bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
4361bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov            return null;
4362bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        }
4363bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        return new Account(name, type);
4364bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov    }
4365bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
4366bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
4367619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
43687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
43697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
43707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
4371d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static class RawContactsEntityIterator implements EntityIterator {
43727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
43737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
43747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
43757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
43767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
43777a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
43787a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
43797a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
43807a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
43817a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
43827a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
43837a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
43847a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
43857a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
43867a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
43877a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
43887a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
43897a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
43907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
43917a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
43927a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
43937a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
43947a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4};
43957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
439646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public static final String[] PROJECTION = new String[]{
43976cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
43986cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
43996cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.SOURCE_ID,
44006cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.VERSION,
44016cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.DIRTY,
440246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts.Entity.DATA_ID,
44037a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RES_PACKAGE,
44047a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.MIMETYPE,
44057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
44067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
44077a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
44087a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
44097a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
44107a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
44117a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
44127a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
44137a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
44147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
44157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
44167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
44177a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
44187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
44197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
44207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
44217a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
44227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
44237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4,
442446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                RawContacts._ID,
44257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.IS_PRIMARY,
44263cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar                Data.IS_SUPER_PRIMARY,
44277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA_VERSION,
44287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                GroupMembership.GROUP_SOURCE_ID,
44297a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC1,
44307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC2,
44317a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC3,
443294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                RawContacts.SYNC4,
443338446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey                RawContacts.DELETED,
4434c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.CONTACT_ID,
4435bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey                RawContacts.STARRED,
4436bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey                RawContacts.IS_RESTRICTED};
4437035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
4438035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
4439035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
4440035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
4441035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
4442035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
4443035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
444467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
444567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
444667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
44477a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_RAW_CONTACT_ID = 27;
44487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_IS_PRIMARY = 28;
44493cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_IS_SUPER_PRIMARY = 29;
44503cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DATA_VERSION = 30;
44513cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_GROUP_SOURCE_ID = 31;
44523cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC1 = 32;
44533cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC2 = 33;
44543cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC3 = 34;
44553cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC4 = 35;
44563cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DELETED = 36;
44573cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_CONTACT_ID = 37;
44583cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_STARRED = 38;
4459bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey        private static final int COLUMN_IS_RESTRICTED = 39;
44607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
446146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        public RawContactsEntityIterator(ContactsProvider2 provider, Uri entityUri,
446246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String contactsIdString,
446346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                String selection, String[] selectionArgs, String sortOrder) {
44647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
446546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            Uri uri;
44667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
446746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, contactsIdString);
446846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = Uri.withAppendedPath(uri, RawContacts.Entity.CONTENT_DIRECTORY);
446946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            } else {
447046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                uri = ContactsContract.RawContactsEntity.CONTENT_URI;
4471035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
447246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            final Uri.Builder builder = uri.buildUpon();
447346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            String query = entityUri.getQuery();
447446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            builder.encodedQuery(query);
447546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            mEntityCursor = provider.query(builder.build(),
447646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    PROJECTION, selection, selectionArgs, sortOrder);
44777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
44787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
44797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4480038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4481038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4482038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4483038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4484038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4485038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4486038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana
44877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
44887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
44897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
44907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
44917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
44927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
44937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
44947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
44957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
44967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
44977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
44987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
44997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
45017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
45027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
45047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
45057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
45067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
45077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
45087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
45097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
45107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
45127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45137a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
45147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
45167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
45176cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
45186cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
45195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
45206cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
45216cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
45226cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
45237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
45247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
45257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
45267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
452794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            contactValues.put(RawContacts.DELETED, c.getLong(COLUMN_DELETED));
452838446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey            contactValues.put(RawContacts.CONTACT_ID, c.getLong(COLUMN_CONTACT_ID));
4529c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            contactValues.put(RawContacts.STARRED, c.getLong(COLUMN_STARRED));
4530bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey            contactValues.put(RawContacts.IS_RESTRICTED, c.getInt(COLUMN_IS_RESTRICTED));
45317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
45327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
45347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
45357a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
45367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
45377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
453823c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                if (c.isNull(COLUMN_CONTACT_ID)) {
453923c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                    continue;
454023c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana//                }
45417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
45427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
454323c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data._ID, c.getLong(COLUMN_DATA_ID));
45447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
45457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
454623c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_PRIMARY, c.getLong(COLUMN_IS_PRIMARY));
454723c48e7953e2f854eeef6d8e3d3c1b901fc571edFred Quintana                dataValues.put(Data.IS_SUPER_PRIMARY, c.getLong(COLUMN_IS_SUPER_PRIMARY));
45487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
45499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
45509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
45519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
45529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
45537a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
45547a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                for (int i = 0; i < DATA_KEYS.length; i++) {
45557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
45567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
45577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
45587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
45597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
45607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
45617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
45627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
45637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
45647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
45657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
45667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
45677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
45687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
45697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
45707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
45717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
45727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
45737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
45747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
45757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4576226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    /**
4577226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
4578226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
4579226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     */
4580226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    private static class GroupsEntityIterator implements EntityIterator {
4581226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private final Cursor mEntityCursor;
4582226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private volatile boolean mIsClosed;
4583226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4584226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final String[] PROJECTION = new String[]{
4585226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups._ID,
4586226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_NAME,
4587226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_TYPE,
4588226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.SOURCE_ID,
4589226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.DIRTY,
4590226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.VERSION,
4591226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.RES_PACKAGE,
4592226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE,
4593226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE_RES,
45947a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.GROUP_VISIBLE,
45957a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC1,
45967a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC2,
45977a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC3,
45987a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC4,
45997a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYSTEM_ID,
460094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.NOTES,
46011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.DELETED,
46021a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey                Groups.SHOULD_SYNC};
4603226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4604226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ID = 0;
4605226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_NAME = 1;
4606226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 2;
4607226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_SOURCE_ID = 3;
4608226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_DIRTY = 4;
4609226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_VERSION = 5;
4610226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_RES_PACKAGE = 6;
4611226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE = 7;
4612226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE_RES = 8;
4613226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_GROUP_VISIBLE = 9;
46147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 10;
46157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 11;
46167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 12;
46177a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 13;
46187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYSTEM_ID = 14;
46197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_NOTES = 15;
462094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        private static final int COLUMN_DELETED = 16;
46211a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        private static final int COLUMN_SHOULD_SYNC = 17;
4622226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4623226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
4624226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
4625226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = false;
4626226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4627226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String updatedSortOrder = (sortOrder == null)
4628226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    ? Groups._ID
4629226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    : (Groups._ID + "," + sortOrder);
4630226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4631b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            final SQLiteDatabase db = provider.mDbHelper.getReadableDatabase();
4632226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4633b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            qb.setTables(provider.mDbHelper.getGroupView());
4634226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setProjectionMap(sGroupsProjectionMap);
4635226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (groupIdString != null) {
4636226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups._ID + "=" + groupIdString);
4637226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4638226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountName = uri.getQueryParameter(Groups.ACCOUNT_NAME);
4639226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountType = uri.getQueryParameter(Groups.ACCOUNT_TYPE);
4640226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!TextUtils.isEmpty(accountName)) {
4641226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups.ACCOUNT_NAME + "="
4642226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4643226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + Groups.ACCOUNT_TYPE + "="
4644226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
4645226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4646226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
4647226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    null, null, updatedSortOrder);
4648226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToFirst();
4649226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4650226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4651226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public void close() {
4652226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4653226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("closing when already closed");
4654226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4655226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = true;
4656226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.close();
4657226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4658226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4659226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public boolean hasNext() throws RemoteException {
4660226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4661226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
4662226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4663226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4664226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return !mEntityCursor.isAfterLast();
4665226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4666226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4667038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4668038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4669038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4670038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4671038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4672038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4673e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4674226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public Entity next() throws RemoteException {
4675226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4676226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
4677226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4678226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!hasNext()) {
4679226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
4680226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4681226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4682226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
4683226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4684226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final long groupId = c.getLong(COLUMN_ID);
4685226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4686226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            // we expect the cursor is already at the row we need to read from
4687226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            ContentValues groupValues = new ContentValues();
4688226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4689226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4690226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups._ID, groupId);
4691226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
4692226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
4693226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4694226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4695226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
4696226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
4697226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
46987a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
46997a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
47007a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
47017a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
47027a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
470394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            groupValues.put(Groups.DELETED, c.getLong(COLUMN_DELETED));
47047a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
47051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            groupValues.put(Groups.SHOULD_SYNC, c.getString(COLUMN_SHOULD_SYNC));
4706226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            Entity group = new Entity(groupValues);
4707226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4708226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToNext();
4709226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4710226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return group;
4711226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4712226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    }
4713226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4714a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
47157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
47167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
4717568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
4718568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
47197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
47207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
47215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS:
47225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID:
47237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
47245ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (match == RAW_CONTACTS_ID) {
47257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
47267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
47277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
472846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                return new RawContactsEntityIterator(this, uri, contactsIdString,
472946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                        selection, selectionArgs, sortOrder);
4730226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS:
4731226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS_ID:
4732226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String idString = null;
4733226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                if (match == GROUPS_ID) {
4734226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    idString = uri.getPathSegments().get(1);
4735226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                }
4736226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4737226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                return new GroupsEntityIterator(this, idString,
4738226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        uri, selection, selectionArgs, sortOrder);
47397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
47407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
47417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
47427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
47437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
47444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
47454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4746a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
47474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4748b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4749b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP:
4750be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
4751b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4752b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4753b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4754f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
4755f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
4756b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4757be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4758b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4759b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4760508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4761b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
476248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
476348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
476448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
476548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
476648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
476748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
476848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
476948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
477048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
477148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
477248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
477348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4774b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4775b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4776b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4777b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4778b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4779b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4780b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4781b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4782c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4783c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4784c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4785c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
478661efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
478761efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
47884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
47894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
47907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
479125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
47923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
479325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
47943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
479525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindNull(1);
47963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
479725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
479825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
479925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
48003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
48013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
480273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
480373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
480473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
480573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
480673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.bindLong(1, rawContactId);
480773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.execute();
480873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
480973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4810c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4811c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
4812c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
4813c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4814c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4815c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4816653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
4817c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
4818653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
4819653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
4820c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
4821c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4822c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
4823c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4824c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
4825c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
4826c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4827c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4828c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4829653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
4830c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
4831653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
4832653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
4833c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
4834c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4835ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4836f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
4837f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
4838f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4839f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4840f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4841f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
4842f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokens.length == 0) {
4843f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4844f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4845f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4846f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String address = tokens[0].getAddress();
4847f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int at = address.indexOf('@');
4848f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (at != -1) {
4849f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            address = address.substring(0, at);
4850f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4851f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4852f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4853f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
4854f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4855f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4856f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4857f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
4858f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4859f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
4860f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
4861f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4862f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4863f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4864f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4865f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
4866f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4867f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4868a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
4869a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
4870a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
4871a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4872a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
4873a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4874a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
4875a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
4876a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
4877a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
4878a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
4879f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4880f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
4881f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
4882f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4883f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4884f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4885f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
4886f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4887f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
4888f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SoftReference<String[]> ref;
4889f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4890f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        synchronized (mNicknameClusterCache) {
4891f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
4892f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
4893f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                if (ref == null) {
4894f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    return null;
4895f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4896f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = ref.get();
4897f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4898f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4899f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4900f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (clusters == null) {
4901f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
4902f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
4903f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
4904f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
4905f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4906f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4907f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4908f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4909f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4910f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
4911b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
4912f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4913f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
4914f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
4915f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                null, null, null);
4916f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
4917f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
4918f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (count > 0) {
4919f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = new String[count];
4920f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int i = 0; i < count; i++) {
4921f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    cursor.moveToNext();
4922f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
4923f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4924f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4925f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        } finally {
4926f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            cursor.close();
4927f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4928f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4929f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4930f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4931f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
4932f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4933f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
4934f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
4935f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4936f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4937f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4938f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
4939f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
4940f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
4941f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4942f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4943f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4944f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
4945f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
4946f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4947f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4948f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4949f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4950f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
4951f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4952f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
4953f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
4954f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
4955f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
4956f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
4957f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
4958f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4959f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4960f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4961f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
4962f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4963f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
4964f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
4965f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
4966f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4967f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
49682d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
4969d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4970d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
4971d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
4972d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
4973d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
4974d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
4975d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
4976e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
4977d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN("
4978d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NAME_COLLATION_KEY + ","
4979d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.EMAIL_BASED_NICKNAME + ","
4980d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.NICKNAME + ","
4981d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                + NameLookupType.ORGANIZATION + "))");
4982e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
4983e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
49845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
4985c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4986c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
4987c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
4988c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4989c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4990a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
4991c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            String limit) {
499220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), limit,
499320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                true);
49945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
49955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
49965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
499720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            String limit, boolean allowEmailMatch) {
4998d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
4999d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + NameLookupColumns.RAW_CONTACT_ID +
5000d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
5001d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
5002d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
50035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
5004a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
5005a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
5006a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
500720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                + NameLookupType.ORGANIZATION);
500820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
500920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
501020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
501120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        sb.append(")");
50123de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
5013c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limit != null) {
5014c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
5015c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5016c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(")");
5017ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
5018ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
50194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
50204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
50214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
50224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5023b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5024b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5025b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5026b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5027b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
50284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
50294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5030b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5031b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5032b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5033caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
50345e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
50355e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
50365e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
50375e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
50385e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
50395e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
50405e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
50415e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
50425e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
50435e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
50445e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5045caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5046caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5047caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
5048df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
5049df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
5050caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5051caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5052caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5053caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
50546f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5055caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
50566f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5057caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5058627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
5059627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    protected boolean isWritableAccount(Account account) {
5060627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5061627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5062627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5063627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
5064627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        account.type.equals(sync.accountType)) {
5065627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    return sync.supportsUploading();
5066627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5067627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5068627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5069627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5070627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
5071627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
5072627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        return false;
5073627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
50744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5075